核心组件

AuthenticationManager,ProviderManager和AuthenticationProvider

AuthenticationManager只是一个接口,实际中是如何运作的?如果我们需要检查多个身份验证数据库或不同身份验证服务(如数据库和LDAP服务器)的组合,该怎么办?

Spring Security中的默认实现为ProviderManager,ProviderManager本身不处理身份验证请求,它会委托给AuthenticationProvider列表进行处理,每个列表都会被查询以查看它是否可以执行认证。每个提供程序将抛出异​​常或返回完全填充的Authentication对象。
org.springframework.security.authentication.ProviderManager内部实现:

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
遍历List<AuthenticationProvider> 列表{
        if(provider.supports(toTest)){
            result = provider.authenticate(authentication);
            this.copyDetails(authentication, result);
        }
}
this.parent.authenticate(authentication);
eventPublisher.publishAuthenticationSuccess(result);
}

还记得我们的好朋友UserDetails和UserDetailsService吗?验证身份验证请求的最常用方法是加载相应的UserDetails并检查加载的密码与用户输入的密码。这是DaoAuthenticationProvider使用的方法(见下文)。加载的UserDetails对象 – 特别是它包含的GrantedAuthority – 将在构建完全填充的Authentication对象时使用,该对象从成功的身份验证返回并存储在SecurityContext中。
DaoAuthenticationProvider继承org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UserDetails user = this.userCache.getUserFromCache(username);
if(user==null){
    user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
}
this.preAuthenticationChecks.check(user);
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
this.postAuthenticationChecks.check(user);

Object principalToReturn = user.getUsername();
return this.createSuccessAuthentication(principalToReturn, authentication, user);
}

AuthenticationProvider实现类:org.springframework.security.authentication.dao.DaoAuthenticationProvider内部实现

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
   throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
} else {
   return loadedUser;
}
}

UserDetailsS​​ervice实现

In-Memory Authentication

<user-service id="userDetailsService">
<!-- Password以{noop}为前缀,指示将其委托给passwordencoder应该使用NoOpPasswordEncoder。这是不安全的生产,但使阅读样本中更容易。通常密码应该使用BCrypt散列 -->
<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
</user-service>

JdbcDaoImpl

从数据库加载

密码编码

历史的密码编码

  • 存储密码原始文本
  • 存储通过单向散列(如SHA-256)的密码
  • 为了降低Rainbow Tables的有效性,鼓励开发人员使用salted密码
  • 现在鼓励开发人员利用自适应单向函数来存储密码。开发人员来设定不同的worker factor满足不同的安全需要,在安全和效率之间做出权衡。例如: bcrypt , PBKDF2 , scrypt , and Argon2 .

DelegatingPasswordEncoder(spring5.0后的新特性)

  • 确保使用当前密码存储建议对密码进行编码
  • 允许验证现代和传统格式的密码
  • 允许将来升级编码

将编码方式存入密码文本

BCryptPasswordEncoder

https://en.wikipedia.org/wiki/Bcrypt

Pbkdf2PasswordEncoder

https://en.wikipedia.org/wiki/PBKDF2

SCryptPasswordEncoder

https://en.wikipedia.org/wiki/Scrypt

版权声明:本文为victor2302原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/victor2302/p/11750407.html