Spring Security 密码登录流程源码分析
初识
Spring Security是通过SecuriyFilterChains过滤器链来保证应用安全的,而这些过滤器链由FilterChainProxy(本质上是个Filter)来管理,每个uri都对应一个SecurityFilterChain,即对应SecurityFilterChain中的Filters。
FilterChainProxy中由SecurityFilterChain维护了很多Filter,debug进入可以看到:
SpringSecurity的密码登录就是由UsernamePasswordAuthenticationFilter过滤器来实现的,先上一张登录校验流程图:
UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter 继承了 AbstractAuthenticationProcessingFilter,doFilter()
方法由其父类实现。
doFilter()
方法中调用了attemptAuthentication()
方法,该方法尝试进行身份验证,由UsernamePasswordAuthenticationFilter
实现,关键代码如下:
ProviderManager
ProviderManager#authenticate() 会获取所有的AuthenticationProvider
,然后遍历,找出与封装的 Token 匹配的 Provider,调用其 authenticate()
方法。
provider.supports方法,以token(authentication)的类型为标准,判断是否是UsernamePasswordAuthenticationToken类或其子类,为扩展UsernamePasswordAuthenticationToken提供了基础。
认证完成后,还会调用eraseCredentials()
方法,进行密码擦除工作,比如把密码置空。
AbstractUserDetailsAuthenticationProvider
ProviderManager#authenticate() 方法,进入AbstractUserDetailsAuthenticationProvider
的 authenticate
方法。
重点关注获取用户信息retrieveUser()
和密码校验additionalAuthenticationChecks()
方法,他们都是抽象方法,由其子类 DaoAuthenticationProvider 实现。
DaoAuthenticationProvider
retrieveUser()
最终调用了 UserDetailsService 的 loadUserByUsername()方法,来获取用户信息,我们可以实现 UserDetailsService 接口,从数据库中查出用户的信息。
additionalAuthenticationChecks()
使用了passwordEncoder.matches()
方法来匹配密码是否相同。
总结
最后再看下这张流程图,就比较清晰了
梳理一下整个认证流程:
通过过滤器获取请求参数,封装成token,调用 ProviderManager 管理的 Provider (认证逻辑的实现类) 的 authenticate
方法,最后调用 UserDetailService
去获取用户的信息,之后做一下前置、后置和密码的校验。
可以留言说下你学习源码的感受,你的评论、在看、转发,都能让我高兴好久。
版权声明: 本文为 InfoQ 作者【读钓】的原创文章。
原文链接:【http://xie.infoq.cn/article/7197009e4095eb0c098302eff】。文章转载请联系作者。
评论