Spring Security Authentication (
本质上讲,Spring Security 是通过过滤器(Filter)和拦截器(Interceptor)实现应用安全控制。Spring Security 中定义和使用了很多的过滤器和拦截器,针对认证过程重点讲解:AbstractAuthenticationProcessingFilter
。
Spring Security 有一个被称作“authentication mechanism”的功能用来收集用户代理(通常是浏览器)提交的用于认证的详细身份信息(通常是用户名和密码),参看 Spring Security Reference 9.4.3 Authentication Mechanism。
AbstractAuthenticationProcessingFilter
用于拦截认证请求,参看 Spring Security API 文档 中对 AbstractAuthenticationProcessingFilter
类的描述就可以发现,它是基于浏览器和 HTTP 认证请求的处理器,可以理解为它就是 Spring Security 认证流程的入口。
整个认证流程如下:
① AbstractAuthenticationProcessingFilter
收集用于认证的用户身份信息(通常是用户名和密码),并基于这些信息构造一个 Authentication
请求对象,AbstractAuthenticationProcessingFilter
只是一个虚类,查看 Spring Security API 文档 可以看到 Spring Security 提供了几个实现类:
CasAuthenticationFilter
OAuth2LoginAuthenticationFilter
OpenIDAuthenticationFilter
UsernamePasswordAuthenticationFilter
最常使用的应该是 UsernamePasswordAuthenticationFilter
,其它类都应用于特定的场景。
② AbstractAuthenticationProcessingFilter
类将构造的 Authentication
请求对象呈现给 AuthenticationManager
,AbstractAuthenticationProcessingFilter
类有以下方法设置和获取 AuthenticationManager
:
protected AuthenticationManager getAuthenticationManager()
public void setAuthenticationManager(AuthenticationManager authenticationManager)
③ AuthenticationManager
只是一个接口,Spring Security 提供了一个默认实现 ProviderManager
。ProviderManager
在接收到 AbstractAuthenticationProcessingFilter
传递过来的 Authentication
请求对象后并不会执行认证处理,它持有一个 AuthenticationProvider
的列表,ProviderManager
委托列表中的 AuthenticationProvider
处理认证请求;
④ AuthenticationProvider
也只是接口,Spring Security 提供了很多此接口的实现,如 DaoAuthenticationProvider
、LdapAuthenticationProvider
、JaasAuthenticationProvider
等,现在暂时不关心这些具体实现。列表中的 AuthenticationProvider
会依次对 Authentication
请求对象进行认证处理,如果认证通过则返回一个完全填充的 Authentication
对象(后面会解释什么是“完全填充”),如果认证不通过则抛出一个异常(注意对抛出的异常有类型要求)或直接返回 null。如果列表中的所有 AuthenticationProvider
都返回 null,则 ProviderManager
会抛出 ProviderNotFoundException
异常;
⑤ 认证通过后 AuthenticationProvider
返回完全填充的 Authentication
对象给 ProviderManager
,ProviderManager
继续向上返回给 AbstractAuthenticationProcessingFilter
,AbstractAuthenticationProcessingFilter
会继续返回。
⑥ Spring Security 的“authentication mechanism”在接收到一个完全填充的 Authentication
对象返回后会认定认证请求有效,并将此 Authentication
对象放入 SecurityContextHolder
。关于 Spring Security “authentication mechanism”的介绍可以参看 Spring Security Reference 9.4.3 Authentication Mechanism
⑦ SecurityContextHolder
是 Spring Security 最基础的对象,用于存储应用程序当前安全上下文的详细信息,这些信息后续会被用于授权。有关 SecurityContextHolder
的更多信息请参看 Spring Security Reference 9.2.1 SecurityContextHolder, SecurityContext and Authentication Objects
至此,Spring Security 的认证流程已介绍完毕,但还缺少对两个十分常见的接口的说明:UserDetails
和 UserDetailsService
。
Spring Security API 文档 对 UserDetails
的说明如下:
Implementations are not used directly by Spring Security for security purposes. They simply store user information which is later encapsulated into Authentication
objects. This allows non-security related user information (such as email addresses, telephone numbers etc) to be stored in a convenient location.
从中可以看出 UserDetails
只是用于存储用户信息并最终封装到 Authentication
对象中。
Spring Security API 文档 对 UserDetailsService
的说明如下:
Core interface which loads user-specific data.
It is used throughout the framework as a user DAO and is the strategy used by the DaoAuthenticationProvider
.
The interface requires only one read-only method, which simplifies support for new data-access strategies.
从中可以看出 UserDetailsService
只定义了一个只读方法,返回一个 UserDetails
接口对象。
UserDetails loadUserByUsername(java.lang.String username)
实际上,UserDetailsService
和 UserDetails
只是构造 Authentication
对象的一个过程。UserDetailsService
可以作为 AuthenticationProvider
的一个属性,在 AuthenticationProvider
执行请求认证时调用 UserDetailsService
的 loadUserByUsername
方法返回一个 UserDetails
对象,并使用此 UserDetails
对象封装最终的 Authentication
对象,事实上这也就是 Spring Security 预置的一些AuthenticationProvider
实现类使用的方法,如 DaoAuthenticationProvider
,可以查看 Spring Security API 文档 进一步了解 DaoAuthenticationProvider
实现细节。
在大多数情况下,如果需要定制认证过程,建议直接实现 AuthenticationProvider
,这样做更有意义,在 AuthenticationProvider
的 authenticate
方法中直接封装 Authentication
对象,这比引用一个 UserDetailsService
实现返回一个 UserDetails
对象后再封装成最终的 Authentication
对象更直观。