本文介绍 Spring Security 在 Servlet 认证中使用的主要架构组件。
目录
- SecurityContextHolder
- 设置身份认证信息
- 访问当前已通过身份认证的用户信息
- SecurityContext
- Authentication
- GrantedAuthority
- AuthenticationManager
- ProviderManager
- AuthenticationProvider
- 带有 AuthenticationEntryPoint 的请求凭证
- AbstractAuthenticationProcessingFilter
SecurityContextHolder
SecurityContextHolder 是 Spring Security 身份认证模型的核心,包含 SecurityContext。
SecurityContextHolder 用于存储 Spring Security 已通过身份认证的用户详情。
Spring Security 并不关心如何填充 SecurityContextHolder,如果它包含一个值,那么它将用作当前经过身份验证的用户。
设置身份认证信息
指示用户已通过身份验证的最简单方法是直接设置 SecurityContextHolder:
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
说明:
- 创建一个空的
SecurityContext对象。注意:如果直接调用SecurityContextHolder.getContext().setAuthentication(authentication)不能避免多线程竞争; - 创建一个
Authentication对象。Spring Security 并不关心设置给SecurityContext的Authentication对象的具体实现,这里使用TestingAuthenticationToken是因为方便使用。生产环境中最常用的应该是UsernamePasswordAuthenticationToken(userDetails, password, authorities)。 - 将
SecurityContext设置给SecurityContextHolder,Spring Security 将使用这些信息进行授权。
访问当前已通过身份认证的用户信息
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
默认情况下 SecurityContextHolder 使用 ThreadLocal 存储这些详情,这意味着 SecurityContext 始终可用于同一执行线程中的方法,即使没有将 SecurityContext 作为这些方法的参数显式传递。可以在启动时配置 SecurityContextHolder 以指定如何存储上下文。
SecurityContext
从 SecurityContextHolder 获取,包含当前通过身份认证(Authenticated)的用户的 Authentication 对象。
Authentication
Authentication 在 Spring Security 中有两个主要用途:
- 作为
AuthenticationManager的输入提供用户提交的用于身份认证的凭据。在此场景下使用时isAuthenticated()方法返回false; - 代表当前已通过身份认证的用户,可以从
SecurityContext中获取当前的Authentication对象。
Authentication 对象 包含:
-
principal:用于识别用户身份,如果使用用户名/密码方式执行的认证,这通常是一个UserDetails实例。 -
credentials:通常是密码,在很多情况下,一旦用户通过身份认证,这部分内容便会被清除以确保不会泄露。 -
authorities:GrantedAuthority集合,是用户被授予的更高级别权限,如角色和范围。
GrantedAuthority
GrantedAuthority 是用户被授予的更高级别权限,如角色和范围。
GrantedAuthority 集合可以通过 Authentication.getAuthorities() 获取。
AuthenticationManager
定义 Spring Security 过滤器如何执行身份认证的 API。认证通过后返回的 Authentication 对象由调用 AuthenticationManager 的控制器(Controller)设置到 SecurityContextHolder 中。
如果不使用 Spring Security 过滤器,可以直接设置 SecurityContextHolder,并不需要使用 AuthenticationManager。
ProviderManager
ProviderManager 是 AuthenticationManager 最常用的实现。ProviderManager 将认证委托给一个 AuthenticationProvider 列表,每个 AuthenticationProvider 都有机会指明认证结果是成功还是失败,又或无法做出认证结果决定并允许下游的 AuthenticationProvider 来做此决定。
如果所有已配置的 AuthenticationProvider 都无法做出认证结果决定,则认证结果即为失败并抛出一个 ProviderNotFoundException 异常。ProviderNotFoundException 异常继承自 AuthenticationException,说明 ProviderManager 未配置支持此类型身份认证。
AuthenticationProvider
被 ProviderManager 用于执行特定类型的身份认证。ProviderManager 中可以注入多个 AuthenticationProvider,每个 AuthenticationProvider 执行一种特定类型的身份认证,例如:
-
DaoAuthenticationProvider:支持基于用户名/密码的基础认证; -
JwtAuthenticationProvider:支持认证 JWT Token。
带有 AuthenticationEntryPoint 的请求凭证
AuthenticationEntryPoint 用于发送 HTTP 响应,此响应包含了请求客户端认证凭证。有时客户端在请求资源时会主动带上认证凭证,在这些情况下 Spring Security 是不需要发送 HTTP 响应要求客户端发送认证凭证,因为已经在之前的请求中带上了。但在另一些情况下,客户端在尚未认证时请求了一些未授权的资源,此时需要发送一个 AuthenticationEntryPoint 实现要求客户端发回认证凭证。
AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter 是执行用户身份认证的最基础的过滤器。
AbstractAuthenticationProcessingFilter 执行认证的流程:
- 用户提交了认证凭证后,
AbstractAuthenticationProcessingFilter从要认证的HttpServletRequest中创建了一个Authentication对象,创建的Authentication对象的类型取决于AbstractAuthenticationProcessingFilter的子类。例如,UsernamePasswordAuthenticationFilter从HttpServletRequest中解析出username和password,并据此创建UsernamePasswordAuthenticationToken; -
Authentication对象被传递给AuthenticationManager进行身份认证; - 如果认证失败将执行以下操作:
-
SecurityContextHolder被清空。 -
RememberMeServices.loginFail被调用,如果未配置Remember Me则不处理。 -
AuthenticationFailureHandler被调用。
-
- 如果认证成功则执行以下操作:
- 通知
SessionAuthenticationStrategy有一个新的登录。 -
Authentication被设置到SecurityContextHolder中,之后SecurityContextPersistenceFilter将SecurityContext保存到HttpSession中。 -
RememberMeServices.loginSuccess被调用,如果未配置Remember Me则不处理 -
ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent事件。
- 通知










