• SpringSecurity系列一:07 认证入口:AbstractAuthenticationProcessingFilter 过滤器


    AbstractAuthenticationProcessingFilter 的职责也就非常明确:处理所有HTTP Request和Response对象,并将其封装成AuthenticationMananger可以处理的Authentication。并且在身份验证成功或失败之后将对应的行为转换为HTTP的Response,同时还要处理一些Web特有的资源比如Session和Cookie。

    AbstractAuthenticationProcessingFilter 源码:

    public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
        
        //事件发布管理器
        protected ApplicationEventPublisher eventPublisher;
        protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
        // 认证管理器,定义了SpringSecurity如何进行认证操作
        // 认证成功后会返回一个Authentication对象,这个对象会被设置到SecurityContextHolder中
        private AuthenticationManager authenticationManager;
        protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
        // 如果用户开启了类似“记住我”之类的免密码登录,RememberMeServices来进行管理。
        private RememberMeServices rememberMeServices = new NullRememberMeServices();
        // 请求匹配器,定义了match()方法,匹配请求HttpServletRequest是否符合定义的规则
        private RequestMatcher requiresAuthenticationRequestMatcher;
        private boolean continueChainBeforeSuccessfulAuthentication = false;
        // 会话验证管理
        private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
        private boolean allowSessionCreation = true;
        // 用户登录成功的后续处理
        private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        // 用户登录失败的后续处理
        private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
        
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
            // 判断是否需要认证
            if (!this.requiresAuthentication(request, response)) {
                // 如果不需要认证,继续执行下一个过滤器
                chain.doFilter(request, response);
            } else {
                Authentication authResult;
                try {
                    // 认证处理,该方法需要子类去重写
                    authResult = this.attemptAuthentication(request, response);
                    if (authResult == null) {
                        return;
                    }
                    // 身份认证成功,保存session
                    this.sessionStrategy.onAuthentication(authResult, request, response);
                } catch (InternalAuthenticationServiceException var8) {
                    this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
                    // 认证失败的处理逻辑
                    this.unsuccessfulAuthentication(request, response, var8);
                    return;
                } catch (AuthenticationException var9) {
                    // 认证失败的处理逻辑
                    this.unsuccessfulAuthentication(request, response, var9);
                    return;
                }
                if (this.continueChainBeforeSuccessfulAuthentication) {
                    chain.doFilter(request, response);
                }
                // 认证成功的处理逻辑
                this.successfulAuthentication(request, response, chain, authResult);
            }
        }
    
         // 判断该filter是否需要处理该次请求,即请求的路径和该filter配置的要处理的url是否匹配
        protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
            return this.requiresAuthenticationRequestMatcher.matches(request);
        }
    
        // 需要子类提供身份认证的具体实现。
        public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;
    
        // 认证成功的处理逻辑
        protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
            // 将认证成功的用户信息保存到 SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(authResult);
            // 处理记住我逻辑
            this.rememberMeServices.loginSuccess(request, response, authResult);
            // 发布时间,即发布认证成功消息,供其他的bean接收和处理
            if (this.eventPublisher != null) {
                this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
            }
    // 认证成功的处理逻辑
                this.successfulAuthentication(request, response, chain, authResult);
            }
        }
    
         // 判断该filter是否需要处理该次请求,即请求的路径和该filter配置的要处理的url是否匹配
        protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
            return this.requiresAuthenticationRequestMatcher.matches(request);
        }
    
        // 需要子类提供身份认证的具体实现。
        public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;
    
        // 认证成功的处理逻辑
        protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
            // 将认证成功的用户信息保存到 SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(authResult);
            // 处理记住我逻辑
            this.rememberMeServices.loginSuccess(request, response, authResult);
            // 发布时间,即发布认证成功消息,供其他的bean接收和处理
            if (this.eventPublisher != null) {
                this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
            }
    		// 认证成功后后续处理
            this.successHandler.onAuthenticationSuccess(request, response, authResult);
        }
    
        // 认证失败的处理逻辑
        protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
            // 清除 SecurityContextHolder 存储的认证信息
            SecurityContextHolder.clearContext();
            // 清除 rememberMe 存储的认证信息
            this.rememberMeServices.loginFail(request, response);
            // 认证失败的后续处理
            this.failureHandler.onAuthenticationFailure(request, response, failed);
        }
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    AbstractAuthenticationProcessingFilter为了完成与浏览器和HTTP请求的验证,它将大任务拆成了几个子任务并交给了以下组件完成:

    • AuthenticationManager用于处理身份验证的核心逻辑;
    • AuthenticationSuccessHandler用于处理验证成功的后续流程;
    • AuthenticationFailureHandler用于处理失败的后续流程;
    • 在验证成功后发布一个名为InteractiveAuthenticationSuccessEvent的事件通知给到应用上下文,用于告知身份验证已经成功;
      因为是基于浏览器所以相关的会话管理行为交由 SessionAuthenticationStrategy来进行实现。
    • 如果用户开启了类似“记住我”之类的免密码登录,AbstractAuthenticationProcessingFilter还有一个名为RememberMeServices来进行管理。

    AbstractAuthenticationProcessingFilte 作为身份认证请求入口,是一个抽象类。OAuth2ClientAuthenticationProcessingFilter(Spriing OAuth2)、RememberMeAuthenticationFilter(RememberMe)都继承了 AbstractAuthenticationProcessingFilter ,并重写了方法 attemptAuthentication 进行身份认证。

    在这里插入图片描述
    UsernamePasswordAuthenticationFilter 过滤器:SpringSecurity 中默认的是表单登录格式,即用户在表单中输入用户名和密码进行登录,登录参数的提取在 UsernamePasswordAuthenticationFilter 过滤器中完成,UsernamePasswordAuthenticationFilter 是AbstractAuthenticationProcessingFilter 针对使用用户名和密码进行身份认证而定制化的一个过滤器。其添加是在调用http.formLogin() 时作用,默认的登录请求 url 为 “/login”,并且为 POST 请求。当我们登录的时候,也就是匹配到loginProcessingUrl,这个过滤器就会委托认证管理器 authenticationManager 来验证登录。

    ClientCredentialsTokenEndpointFilter 过滤器:Spring Security对于获取TOKEN的请求(默认是"/oauth/token"),需要认证client_id和client_secret。认证client_id和client_secret可以有2种方式,一种是通过本节讲的ClientCredentialsTokenEndpointFilter,另一种是通过BasicAuthenticationFilter。ClientCredentialsTokenEndpointFilter首先比对请求URL是否是TOKEN请求路径以及请求参数中是否包含client_id,如果满足以上条件,再调用ProviderManager认证client_id和client_secret是否与配置的一致。如果通过认证,会把身份认证信息保存打SecurityContext上下文中。

    OAuth2AuthenticationProcessingFilter 过滤器:授权认证服务通过认证后会返回 Access Token,该token可用于请求资源服务(业务系统)的接口。我们需要把特定的信息放到请求头中,例如在请求头中写入Authorization: Bearer !xBYUEBY0N3o234N,Authorization为key,Bearer !xBYUEBY0N3o234N为value,!xBYUEBY0N3o234N是Access Token。请求经过OAuth2AuthenticationProcessingFilter后,过滤器中的认证管理器会调用配置的授权认证服务的check token接口校验token是否有效,token有效则可以继续访问了。需要注意的是,对于资源认证服务Spring Security会把这个过滤器加入到过滤链里,但授权认证认证服务却不能。

  • 相关阅读:
    【设计模式】单例模式
    大厂面试题-MVCC的理解
    C++ partition()和stable_partition()函数用法详解(深入了解,一文学会)
    直播软件App开发:10个关键步骤,从零到一掌握
    【设计模式从青铜到王者】第四篇:创建型模式
    【论文阅读】机器翻译新范式,《A Paradigm Shift in Machine Translation》
    敏感信息防泄漏:透明加密与通信内容安全策略深度解析
    如何在Linux环境中远程访问企业级夜莺监控分析工具?
    洛谷刷题C语言:Bold、饱食、公平の意、DOM、
    npy和npz里的图片分解(格式讲解)!超级清晰版本
  • 原文地址:https://blog.csdn.net/qq_42764468/article/details/126844833