spring-security认证的过程
认证过程
1、接收登录请求,获取用户密码参数,构建UsernamePasswordAuthenticationToken对象
2、通过注解的authenticationManager对象的authenticate的方法进行验证(实际由其实现类ProviderManager完成)
验证的步骤:
在ProviderManager的authenticate方法中,循环成员变量List<AuthenticationProvider> providers,
该providers中如果有一个AuthenticationProvider的supports函数返回true,那么就会调用该AuthenticationProvider的authenticate函数认证,AuthenticationProvider的认证过程,通过关联的UserDetailService的loadUserByUsername获取该用户信息,再进行各种图上的各种比对。如果认证成功则整个认证过程结束。如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证成功则为认证成功。
AuthenticationProvider与UserDetailService的配置关系(以下内容依次被覆盖)
1、默认配置,在InitializeUserDetailsBeanManagerConfigurer配置中UserDetailService实现类配置的@service默认关联到DaoAuthenticationProvider
2、采用globalUserDetails方式配置默认的UserDetailService到关联的默认的DaoAuthenticationProvider
3、采用新建AuthenticationProvider,在AuthenticationManagerBuilderbuilder添加的方式
4、采用新建AuthenticationProvider,并在SecurityConfigurerAdapter里面注册的方式
spring-security验证信息的存储
1、存储的位置
SecurityContextHolder.getContext().setAuthentication(authentication)
认证信息都是存储在SecurityContextHolder的ThreadLocal中,但是ThreadLocal是只能保证在他同一个线程中读取,问题是现在不同的线程访问,怎么获取登录的用户信息。这是通过SecurityContextPersistenceFilter实现的。
SecurityContextPersistenceFilter实现原理
主要是获取session持久化的SecurityContext对象,并配置到SecurityContextHolder中,保证当前线程在中的SecurityContext对象一致,执行完毕后,再将当前线程中的SecurityContext对象删除,重新持久化session的SecurityContext对象。
通过配置的SecurityContextRepository,实现不同的方式管理当前的用户,以HttpSessionSecurityContextRepository为例:
获取当前用户的SecurityContext对象,根据session中的持久化中获取到当前用户的SecurityContext对象,并在SecurityContextHolder里面设置到当前线程中,确保在当前线程中,获取的是同一个SecurityContext对象,在执行完毕后,再SecurityContext实例持久化到session中,最后把SecurityContextHolder在当前线程中的SecurityContext对象删除(因为该线程再也不会被使用,减少GC)。
2、存储的结构
- 用户权限集合 => 可用于访问受保护资源时的权限验证
- 用户证书(密码) => 初次认证的时候,进行填充,认证成功后将被清空
- 细节 => 暂不清楚,猜测应该是记录哪些保护资源已经验证授权,下次不用再验证,等等。
- Pirncipal => 用户详细信息 继承UserDetailes的类
- 是否已认证成功