UsernamePasswordAuthenticationFilter

  UsernamePasswordAuthenticationFilterAbstractAuthenticationProcessingFilter针对使用用户名和密码进行身份验证而定制化的一个过滤器。

  UsernamePasswordAuthenticationFilter继承扩展AbstractAuthenticationProcessingFilter,相对与AbstractAuthenticationProcessingFilter主要有以下几个改动:

  
属性中增加了username和password字段;
强制的只对POST请求应用;
重写了attemptAuthentication身份验证入口方法。

  重写attemptAuthentication的方法主要是完了构建一个UsernamePasswordAuthenticationToken对象并且将其传递给AuthenticationManager进行身份验证。

         UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,password);
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);

UsernamePasswordAuthenticationToken

  UsernamePasswordAuthenticationToken是整个身份验证流程封装身份验证请求数据中的数据对象。 UsernamePasswordAuthenticationFilter

       需要从HttpRequest中获取对应的参数字段,将其封装进Authentication中传递给AuthenticationManager进行身份验证,Authentication是一个接口声明,一个特定行为的声明,并不是一个类,没有办法实例化为对象进行传递。在UsernamePasswordAuthenticationFilter的身份验证设计里,验证协议可以描述为:一组用户名和密码,如果匹配,那么就算验证成功。用户名即是一个唯一可以标识不同用户的字段,而密码则是检验当前的身份验证是否正确的凭证信息。在Spring Security中便将使用username和password封装成Authentication的实现声明为UsernamePasswordAuthenticationToken

封装用户名和密码

  UsernamePasswordAuthenticationToken继承AbstractAuthenticationToken抽象类,主要与AbstractAuthenticationToken的区分就是针对使用用户名和密码验证的请求按照约定进行一定的封装:将username赋值到principal ,而将password赋值到credentials。

public UsernamePasswordAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) {
    super(authorities);
    this.principal = principal;
    this.credentials = credentials;
    super.setAuthenticated(true); // must use super, as we override
}

  通过UsernamePasswordAuthenticationToken实例化Authentication接口,继而按照流程,将其传递给AuthenticationManager调用身份验证核心完成相关工作。

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

// Allow subclasses to set the "details" property
setDetails(request, authRequest);

return this.getAuthenticationManager().authenticate(authRequest);

  Authentication接口实现类UsernamePasswordAuthenticationToken通过AuthenticationManager提供的验证方法作为参数被传递到身份验证的核心组件中。AuthenticationManager接口设计上并不是用于完成特定的身份验证工作的,而是调用其所配发的AuthenticationProvider接口去实现的。针对不同验证协议的AuthenticationProvider的实现类们是完成对应的工作的,并且AuthenticationManager是如何知道应该使用哪一个AuthenticationProvider才能完成对应协议的验证工作?AuthenticationProvider接口的声明

  

   AuthenticationProvider只包含两个方法声明:一个是用于验证身份请求AuthenticationToken的authenticate方法,另外是辨别当前AuthenticationProvider是否是完成相应验证工作的supports方法

   Spring Security中唯一AuthenticationManager实现类ProviderManager,在处理authenticate身份验证入口方法,第一解决的问题:

哪个AuthenticationProvider能验证当前传入AuthenticationProviderManager会对其所有的AuthenticationProvider做supports方法检测,直到有AuthenticationProvider能在supports方法被调用后返回true
   UsernamePasswordAuthenticationFilter已经封装一个UsernamePasswordAuthenticationToken,并将它传递给了ProviderMananger。随后ProviderMananger便会一次轮训它管理的所有AuthenticationProvider,询问是否有谁能支持这个Authentication的实现类。
  
  AuthenticationProvider开发使用用户名和密码与UserDetailsService交互并且验证密码的DaoAuthenticationProviderDaoAuthenticationProvider是AbstractUserDetailsAuthenticationProvider的实现类,DaoAuthenticationProviderUsernamePasswordAuthenticationToken的逻辑都是通过AbstractUserDetailsAuthenticationProvider完成的。比如针对ProviderManager询问是否支持当前Authentication的supports方法:
    public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

       完成是否支持的supports验证后,ProviderMananger会全权将验证工作交由DaoAuthenticationProvider进行处理。与ProviderMananger最不同一点是,在DaoAuthenticationProvider的视角里,当前的Authentication最起码一定是UsernamePasswordAuthenticationToken的形式, 在DaoAuthenticationProvider分别会按照预先设计一样分别从principal和credentials获取用户名和密码进行验证。

String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();

String presentedPassword = authentication.getCredentials().toString();

  接着便是通过UserDetailsService使用username获取对应的UserDetails,最后通过对比密码是否一致,向PrivoderManager返回最终的身份验证结果与身份信息。这样一个特定场景使用用户名和密码的验证流程就完成了。

  


  

  

  

  
posted on 2022-08-05 07:16  溪水静幽  阅读(219)  评论(0)    收藏  举报