Java教程

Spring-security源码-Filter之FilterSecurityInterceptor(十八)

本文主要是介绍Spring-security源码-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 {
            //继续放行 因为和这个是最后一个过滤器 所以我们看 放行的过滤器
            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;
        }
        //<4>如果未认证则交给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;
        }
    }

 

这篇关于Spring-security源码-Filter之FilterSecurityInterceptor(十八)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!