在继续深入向下的学习前,有必要理解清楚 Spring Security 的认证流程,这样才能理解为什么要这样写代码,也方便后续的扩展。
SpringSecurity的本质是一个过滤器链,以上列举了几个核心的过滤器,我们通过debugger的方式查看一下SpringSecurity完整的过滤器:
这就是SpringSecurity完整的过滤器链,总共有16个,不需要每个都去了解,在这里我们只讲几个重点的过滤器。
上图是 Spring Security 认证流程的一部分,下面的讲解以上图为依据。
1. 用户发起表单登录请求后,首先进入 UsernamePasswordAuthenticationFilter
说明: 我们看以上源代码可知,在这个UsernamePasswordAuthenticationFilter 针对post方式的/login请求进行拦截,在这里面主要就是做了一件事,就是将前端传来的用户名,密码构建出一个UsernamePasswordAuthenticationToken 对象,这个对象是Authentication的一个子类,Authentication就是认证对象
在UsernamePasswordAuthenticationFilter值得关注的就是构建UsernamePasswordAuthenticationFilter的时候是这样的:
2. 接下来将这个没有认证的对象UsernamePasswordAuthenticationToken 交给AuthenticationManager 来处理:
AuthenticationManager 内部不做任何的认证逻辑处理,其核心和管理所有的AuthenticationProvider
3.接下来由AuthenticationManager 的实现类ProviderManager来挑选由谁来处理认证逻辑:
说明: Spring Security 支持多种认证逻辑,每一种认证逻辑的认证方式其实就是一种 AuthenticationProvider。通过 getProviders() 方法就能获取所有的 AuthenticationProvider,通过 provider.supports() 来判断 provider 是否支持当前的认证逻辑。当选择好一个合适的 AuthenticationProvider 后,通过 provider.authenticate(authentication) 来让 AuthenticationProvider 进行认证
用户名密码的认证方式是由AbstractUserDetailsAuthenticationProvider 进行处理的:
在AbstractUserDetailsAuthenticationProvider内部通过retrieveUser来获取 UserDetails对象,retrieveUser() 的具体实现在 DaoAuthenticationProvider 中:
loadUserByUsername是由UserDetailsService接口提供的方法,默认实现是SpringSecrity在内存中读取的,后期我们做自己的项目可以写个实现类去重写loadUserByUsername方法,在里面自定义我们获取用户的方式,一般是自己写查询数据库的逻辑。在AbstractUserDetailsAuthenticationProvider 中我们成功获取到了UserDetails对象我们就可以进行认证了:
说明: 在上图中,我们可以看到认证校验分为 前校验、附加校验和后校验,如果任何一个校验出错,就会抛出相应的异常。所有校验都通过后,调用
createSuccessAuthentication() 返回认证信息。
4. 至此认证信息就被传递回 UsernamePasswordAuthenticationFilter 中,在 UsernamePasswordAuthenticationFilter 的父类 AbstractAuthenticationProcessingFilter 的 doFilter() 中,会根据认证的成功或者失败调用相应的 handler:
5. successfulAuthentication()方法中:
查看 SecurityContext 源码,发现内部就是对 Authentication 的封装,提供了 equals、hashcode、toString等方法,而SecurityContextHolder 可以理解为线程中的 ThreadLocal。
我们知道一个 HTTP 请求和响应都是在一个线程中执行,因此在整个处理的任何一个方法中都可以通过 SecurityContextHolder.getContext() 来取得存放进去的认证信息。
从 Session 中对认证信息的处理由 SecurityContextPersistenceFilter 来处理,它位于 Spring Security 过滤器链的最前面,它的主要作用是:
通过调用 SecurityContextHolder.getContext().getAuthentication() 就能够取得认证信息:
@GetMapping("/me")
@ResponseBody
public Object me() {
return SecurityContextHolder.getContext().getAuthentication();
}
以上便是SpringSecurity的认证流程,后续会根据这个流程去改造整合进我们自己项目