本质上讲,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 对象更直观。