AbstractAuthenticationProcessingFilter

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

  

 

 

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

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

  

问题1. 怎么判断当前的请求是需要被验证访问的?

  在正式进行身份之前,doFilter会通过Security中的\color{red}{RequestMatcher}。尝试查找是否有匹配记录

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // inde.html对应的url允许所任人访问
                .antMatchers("/").permitAll()
                // user.html对应的url,则需要用户有USER的角色才可以访问
                .antMatchers("/user").hasRole("USER")
                .and()
                .formLogin();
    }

问题2. 如何进行身份验证?

  doFilter中通过调用自己的attemptAuthentication方法,但并不进行身份验证的逻辑处理,而是委托AuthenticationManager去完成相关的身份验证流程。AbstractAuthenticationProcessingFilter将HttpServletRequest包装成了Authentication对象与核心的AuthenticationManager进行交互。这样的设计可以使AuthenticationManager不感知外部的Web环境,从而使Security不仅可以支持Web应用,同时也可以被所有Java应用进行使用——只要客制化外部参与并将其封装成Authentication与AuthenticationManager的进行身份验证。

        注意在AuthenticationManager中实际完成身份验证任务并不是AuthenticationManager自身。而是将相关的任务针对每一种身份验证协议的AuthenticationProvider去完成相关的身份验证工作。       

问题3. 如何进行会话验证管理?动机是什么?

  

 

   HTTP的请求实际上是无状态的,浏览器为了使HTTP之间能使用同一会话进行操作,在Java Web中通常会将Web容器(特指Tomcat)的JSESSIONID写入请求的cookie中一同发送。而服务端中通过中的JSESSIONID是通过request.getSession().getId()获取的,这样便使本来无状态的HTTP通过客户端的cookie中JSESSIONID与服务端的Session关联了起来。但是从安全角度来说这样匹配机制存在许多问题,最简单的问题就是Session id在整个会话失效之间是不会变更的,这样就可以通过身份验证通过后获取了Session id从而通过其他客户端伪造cookie与服务端进行交互。

问题4. 成功和失败的后续流程都在干什么?

  

        验证成功之后AuthenticationManager会返回一个通过UserDetail构造并且附带上了所有授权信息的Authentication对象。 而验证失败的话则会抛出\color{red}{AuthenticationException},AbstractAuthenticationProcessingFilter捕获异常之后进行进行验证失败的处理。 成功的后续操作最主要的一个操作便是,通过SecurityContextHolder将本次验证之后的Authentication对象塞到当前的SecurityContext中。在后续的操作中如需要使用到Authentication身份信息,则可以直接通过SecurityContextHolder去获取。

     //成功后设置上下文二
    SecurityContextHolder.getContext().setAuthentication(authResult);

     //后续操作可以从上下文中获取身份信息
     SecurityContextHolder.getContext().getAuthentication();

  再通过ApplicationEventPublisher发送验证成功的事件信息供其他相关监听器进行相关操作。成功是重新将最新的Authentication对象塞到SecurityContext上下文中,失败便是直接清空了上下文,让其Authentication对象变得“一无所有”。

  对于request的额外操作则可以分别通过\color{red}{AuthenticationSuccessHandler}\color{red}{AuthenticationFailureHandler}两个接口去设置相关操作。

Remember-Me功能实现流程是什么?

  Remember-Me是指网站能够在Session之间记住登录用户的身份,具体来说就是我成功认证一次之后在一定的时间内我可以不用再输入用户名和密码进行登录了,系统会自动给我登录。这通常是通过服务端发送一个cookie给客户端浏览器,下次浏览器再访问服务端时服务端能够自动检测客户端的cookie,根据cookie值触发自动登录操作。

  Remember-Me机制的现实是依赖浏览器Cookie的,在默认情况下SpringSecurity会将编码后的字符串存于Cookie中的remember-me键位。

如何在其他服务中监听验证成功的事件?

  AbstractAuthenticationProcessingFilter还会通过Spring容器的\color{red}{ApplicationEventPublisher}事件发布器发射一个\color{red}{InteractiveAuthenticationSuccessEvent}。如果需要在应用其他监听器上处理相关验证成功后操作。我们可以通过Spring中的@EventListener监听InteractiveAuthenticationSuccessEvent事件便可以实现。
  想在每个用户登录成功后,在控制台打印出登录用户的用户名。
@Component
public class AuthenticationListener {
    @EventListener
    public void register(InteractiveAuthenticationSuccessEvent event)
    {
        //获取登录成功的Authentication对象
        Authentication authentication = event.getAuthentication();
        //打印用户名
        System.out.println("@EventListener注册信息,用户名:"+authentication.getName());
    }
}
参考:
https://juejin.cn/post/6844903806778474504

  
posted on 2022-07-30 11:42  溪水静幽  阅读(905)  评论(0)    收藏  举报