• Springsecurity源码Filter之FilterSecurityInterceptor(十九)


    FilterSecurityInterceptor最后一个过滤器,主要做认证和授权拦截,比如我们未登录访问需要登录的页面或者我们配置了授权的页面

    http.authorizeRequests() .antMatchers("/hello").hasRole("admin").antMatchers("/hello2").hasAnyAuthority("au-test")

    初始化处
    org.springframework.security.config.annotation.web.configurers.AbstractInterceptUrlConfigurer#configure

      @Override
        public void configure(H http) throws Exception {
    
            /**
             * 调用子类获取FilterInvocationSecurityMetadataSource
             * 主要是为了管理我们配置 url映射 对应的配置比如antMatchers("/hello").hasRole("admin")
             * 后续就可以快速通过url获取对应的匹配配置
             */
            FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
            if (metadataSource == null) {
                return;
            }
            //创建FilterSecurityInterceptor
            FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource,
                    http.getSharedObject(AuthenticationManager.class));
            if (this.filterSecurityInterceptorOncePerRequest != null) {
                securityInterceptor.setObserveOncePerRequest(this.filterSecurityInterceptorOncePerRequest);
            }
            //注入
            securityInterceptor = postProcess(securityInterceptor);
            //加入到Fitler
            http.addFilter(securityInterceptor);
            http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
        }

    org.springframework.security.web.access.intercept.FilterSecurityInterceptor#doFilter

      @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            //<1>通过FilterInvocation封装
            invoke(new FilterInvocation(request, response, chain));
        }

    <1>

    org.springframework.security.web.access.intercept.FilterSecurityInterceptor#invoke

      public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
            //是否需要验证权限和会话
            if (isApplied(filterInvocation) && this.observeOncePerRequest) {
                // filter already applied to this request and user wants us to observe
                // once-per-request handling, so don't re-do security checking
                filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
                return;
            }
            // first time this request being called, so perform security checking
            if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
                filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }
            //<2>调用DispatcherFilter之前验证
            InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
            try {
                //继续放行 因为和这个是最后一个过滤器 所以我们看 放行的过滤器<5>
                filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
            }
            finally {
                super.finallyInvocation(token);
            }
            //调用Dispatcher之后验证
            super.afterInvocation(token, null);
        }

    <2>


    org.springframework.security.access.intercept.AbstractSecurityInterceptor#beforeInvocation

     /**
         * Object封装了我们的Url
         * @param object
         * @return
         */
        protected InterceptorStatusToken beforeInvocation(Object object) {
            Assert.notNull(object, "Object was null");
            if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
                throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName()
                        + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
                        + getSecureObjectClass());
            }
            //根据Url获取ConfigAttribute 就是我们配置的url的hasRole的运行是数据结构 通过ConfigAttribute封装
            Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
            //如果没有配置则不校验
            if (CollectionUtils.isEmpty(attributes)) {
                Assert.isTrue(!this.rejectPublicInvocations,
                        () -> "Secure object invocation " + object
                                + " was denied as public invocations are not allowed via this interceptor. "
                                + "This indicates a configuration error because the "
                                + "rejectPublicInvocations property is set to 'true'");
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Authorized public object %s", object));
                }
                publishEvent(new PublicInvocationEvent(object));
                return null; // no further work post-invocation
            }
            //如果没有用户信息抛错由ExceptionTranslationFilter 捕获处理
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
                        "An Authentication object was not found in the SecurityContext"), object, attributes);
            }
            //<3>有用户信息 校验是否认证通过
            Authentication authenticated = authenticateIfRequired();
            if (this.logger.isTraceEnabled()) {
                this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes));
            }
            //<4>根据权限配置 进行权限校验
            attemptAuthorization(object, attributes, authenticated);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes));
            }
            if (this.publishAuthorizationSuccess) {
                publishEvent(new AuthorizedEvent(object, attributes, authenticated));
            }
    
            // A权限通过的处理器 我们应该可以根据这个重新设置用户信息
            Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
            if (runAs != null) {
                SecurityContext origCtx = SecurityContextHolder.getContext();
                SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                SecurityContextHolder.getContext().setAuthentication(runAs);
    
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
                }
                // need to revert to token.Authenticated post-invocation
                return new InterceptorStatusToken(origCtx, true, attributes, object);
            }
            this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
            // no further work post-invocation
            return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
    
        }

    <3>

     private Authentication authenticateIfRequired() {
            //获得用户信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication.isAuthenticated() && !this.alwaysReauthenticate) {
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(LogMessage.format("Did not re-authenticate %s before authorizing", authentication));
                }
                return authentication;
            }
            //如果未认证则交给authenticationManager 重新认证
            authentication = this.authenticationManager.authenticate(authentication);
            // Don't authenticated.setAuthentication(true) because each provider does that
            if (this.logger.isDebugEnabled()) {
                this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication));
            }
            //set到Authentication
            SecurityContextHolder.getContext().setAuthentication(authentication);
            return authentication;
        }

    <4>

        private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
                                          Authentication authenticated) {
            try {
                //交给accessDecisionManager  感兴趣可以打断点继续查看比如通过eval 表达式 或者对应的处理类获取Authentication的权限或则判断 
                this.accessDecisionManager.decide(authenticated, object, attributes);
            }
            catch (AccessDeniedException ex) {//权限认证未通过抛出异常 由ExceptionTranslationFilter 捕获处理
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(LogMessage.format("Failed to authorize %s with attributes %s using %s", object,
                            attributes, this.accessDecisionManager));
                }
                else if (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Failed to authorize %s with attributes %s", object, attributes));
                }
                publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
                throw ex;
            }
        }

    <5>

    可以参考https://www.cnblogs.com/LQBlog/p/15508248.html#autoid-11-0-0

    secrityFilter最终由FilterChainProxy封装作为入口

    @Override
        protected Filter performBuild() throws Exception {
            Assert.state(!this.securityFilterChainBuilders.isEmpty(),
                    () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
                            + "Typically this is done by exposing a SecurityFilterChain bean "
                            + "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
                            + "More advanced users can invoke " + WebSecurity.class.getSimpleName()
                            + ".addSecurityFilterChainBuilder directly");
            /**
             * 得我们设置的忽略检查为他们添加一个filter
             *  public void configure(WebSecurity web) throws Exception {
             *         web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
             *     }
             */
            int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
            List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
            for (RequestMatcher ignoredRequest : this.ignoredRequests) {
                securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
            }
            //securityFilterChainBuilders为HttpSecurity<6>处初始化
            for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
                //执行build<1> 最终会构建成<13>
                securityFilterChains.add(securityFilterChainBuilder.build());
            }
            //通过FilterChainProxy 代理管理 它实现了ServletFilter 通过FilterChainProxy为Servlet入口 进入security的自己的filter
            FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
            if (this.httpFirewall != null) {
                filterChainProxy.setFirewall(this.httpFirewall);
            }
            if (this.requestRejectedHandler != null) {
                filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
            }
            filterChainProxy.afterPropertiesSet();
    
            Filter result = filterChainProxy;
            if (this.debugEnabled) {
               
                result = new DebugFilter(filterChainProxy);
            }
            this.postBuildAction.run();
            //返回filter 我们请求都会到filterChainProxy  通过他调用security的filter实现securityfilter 注入逻辑
            return result;
        }

    FilterChainProxy的doFilter最终是委托给VirtualFilterChain

    org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter

     @Override
        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            //表示是最后一个Filter
            if (this.currentPosition == this.size) {
                if (logger.isDebugEnabled()) {
                    logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));
                }
                // Deactivate path stripping as we exit the security filter chain
                this.firewalledRequest.reset();
                //originalChain 就是我们的DispatcherFilter
                this.originalChain.doFilter(request, response);
                return;
            }
            //不是最后一个则依次++遍历所有Filter
            this.currentPosition++;
            Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
            if (logger.isTraceEnabled()) {
                logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),
                        this.currentPosition, this.size));
            }
            nextFilter.doFilter(request, response, this);
        }
  • 相关阅读:
    Educational Codeforces Round 75 (Rated for Div. 2)
    Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2)
    Codeforces Round #594 (Div. 2)
    Codeforces Round #597 (Div. 2)
    Codeforces Round #599 (Div. 2)
    数学笔记
    模板
    模板
    win7如何更改语言教程
    cmd-net命令详解
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15547321.html
Copyright © 2020-2023  润新知