AuthenticationManager

       Spring Security核心组件用

  

 

AuthenticationMananger与ProviderMananger

  AuthenticationMananger作为整个身份验证核心最外层的封装负责与外部使用者进行交互。AuthenticationMananger接口有且仅有一个对外的服务便是“身份验证”。

    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

  外部使用者通过将身份验证的必要信息,比如用户名和密码封装一个Authentication传递、调用AuthenticationMananger的authenticate方法。如果没有返回异常和null值,验证服务便是完成。完成身份验证的Authentication不仅包含用户的身份验证信息,比如用户名,还会将该用户身份下所有对应的权限列表也一并封装返回。

身份信息交互的纽带:Authentication

  整个与外部使用交互的过程中Authentication的职责有两个,第一个是封装验证请求的参数,第二个便是封装用户的权限信息。结合Authentication的接口设计更加清晰这样的设计意图:principal用于存放用户的身份标识信息,比如用户名,credentials用于存放用户的验证凭证比如密码,authorities用于存放用户的权限列表。而details则存放除了用户名和密码其他可能会被用于身份验证的信息,比如应用限定用户的使用ip范围场景下,ip信息可能便会被存放在details做辅助的验证信息使用。

  为了向外部提供身份验证服务,Spring Security通过ProviderMananger实现AuthenticationManager的身份验证接口。在ProviderMananger为了管理外部输入与向外部返回的AuthenticationProviderMananger内部大致的工序如下:
  • 首先,寻找可以进行验证当前外部输入Authentication形式的AuthenticationProvider;如果自身的providers中无法处理验证并且当前层次的Mananger还有父级的Mananger则向上传递,交由父层Mananger进行处理;
  • 然后,因为details的信息是外部传入的,内部身份验证后的Authentication并不会从持久化或者其他数据源中携带,在返回前将details写入返回给外部的Authentication
  • 最后,如果有必要则将外部身份验证请求中的敏感擦除,比如讲请求验证的密码置空。

  整个验证框架只有一个ProviderManager暴露在外部,但是其内部可能是有多个AuthenticationManangerAuthenticationProvider组成的网络,并且最终进行核心身份验证的还是AuthenticationProvider。核心在叶子节点中依次寻找对验证当前Authentication形式的AuthenticationProvider。如果存在支持便将验证请求的Authentication传递给AuthenticationProvider,委托其进行验证。在处理输入的验证请求AuthenticationProviderMananger并不对其进行任何的处理,而是指在处理完后进行必要的加工和处理。

  

AuthenticationProvider视角中的Authentication

  Authentication主要职责就是封装身份验证时候需要的信息数据,比如用户名场景下的用户名和密码,短信验证码下的手机号码和验证码,OAuth2场景下的ID和Code。总之每个不同验证协议使用的验证信息都需要被被封装成Authentication,更准确说在Spring Security把这种封装用户身份验证信息的Authentication具体为\color{red}{AuthenticationToken}的概念。所有Spring Security中提供的各种协议的身份验证数据的封装都继承\color{red}{AbstractAuthenticationToken},基于用户名和密码的UsernamePasswordAuthenticationToken,基于OAuth2的OAuth2AuthorizationCodeAuthenticationToken,基于CAS的CasAssertionAuthenticationToken。

DaoAuthenticationProvider

  

   DaoAuthenticationProvider为了实现外部的验证请求便需要对外部传递身份信息——用户名和密码进行验证。

从数据层获取对应用户名在数据层的数据记录;
对外部的用户名、密码与数据层的用户名、密码进行比对。

  第一个任务,从数据层获取对应用户名在数据层的数据记录,我们的目标是从数据层中查找到我们需要比对的用户身份数据,但是在这个场景下我们无非控制的是数据层的实现具体是什么?是通过JDBC访问Mysql还是通过JPA访问Oracle,更或是直接通过内存访问一个存储了用户信息键值对的Map? 第二个任务,对外部的用户名、密码与数据层的用户名、密码进行比对,具体的加密算法是什么?如何实现的? 这两个问题在DaoAuthenticationProvider中都无法给出明确的实现。Spring Security便将这两种在DaoAuthenticationProvider无法确定、存在变化的行为分别委托给了DaoAuthenticationProvider两个重要组件去完成:

  1. 通过用户名返回数据层中的用户信息的UserDetailsService;
  2. 通过特定加密算法处理用户密码的PasswordEncoder。

使用UserDetailsService获取内部用户身份信息

  UserDetailsService接口定位向核心组件们提供数据层的用户信息,而用户信息在这里被封装成UserDetails。

  

 

   UserDetailsService中只有一个方法是loadUserByUsername方法,通过传入用户名返回数据层用户身份记录。当确定获取用户身份信息的方法之后,便可以自行扩展UserDetailsService方法,告知框架如何获取用户身份信息。这个步骤是使用Spring Security中是必须完成的工作。 以下代码用于配置使用的UserDetailsService:

public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {
    //注入新的UserDetailsServiceBean
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        return manager;
    }
}

  通过UserDetailsService返回数据类型是UserDetails,UserDetails中封装用户名、密码和授权信息同时还额外包括一些过期和锁定的标识属性。不难发现UserDetails封装的数据和Authentication非常的相似。在身份验证成功后,DaoAuthenticationProvider便会将内部的UserDetails抽离必要的数据对应赋值到UsernamePasswordAuthenticationToken最终返回给外部调用者进行使用

使用PasswordEncoder来进行密码的比对

  DaoAuthenticationProvider收到了外部提交的用户名和密码,同样的DaoAuthenticationProvider也查找到了对应用户名在数据库中的用户名和密码。通常情况下虽然都是密码,数据库中存储的密码通常会进行过一定的加密。DaoAuthenticationProvider便需要将外部提交的用户名和密码进行一次加密流程并进行比对。PasswordEncoder接口主要的作用就是对明文密码进行加密与比对。

  Spring Security中默认向DaoAuthenticationProvider提供的PasswordEncoder是BCryptPasswordEncoder。

  

  

  

  
  
posted on 2022-07-30 21:00  溪水静幽  阅读(3499)  评论(0编辑  收藏  举报