spring security原理-学习笔记2-核心组件

核心组件

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

参考

英文版文档
https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/htmlsingle/#overall-architecture
中文版文档
https://www.springcloud.cc/spring-security.html

posted @ 2019-10-28 10:05  聚变归来  阅读(290)  评论(0编辑  收藏  举报