- 一、 AuthenticationManager 接口
 - 二、Authentication接口
 - 三、SecurityContextHolder
 - Spring Security 中的 
SecurityContextHolder详解 - 1.SecurityContextHolder 作用
 - 2. SecurityContextHolder 的结构
 - 3. SecurityContext 与 Authentication
 - 4. SecurityContextHolder 认证流程
 - 5. SecurityContextHolder 的存储策略
 - 6. SecurityContextHolder 的常见使用场景
 - 7. 线程安全问题
 - 8. 总结
 
在Spring Security中认证(Authentication)和授权(Authorization)是独立的,是分开的。一切授权操作都是在认证成功后的基础之上进行的,如果身份不合法,那也就没有授权的必要了。

一、 AuthenticationManager 接口
1. AuthenticationManager是什么?
AuthenticationManager 是一个接口,它定义了一个核心方法:
Authentication authenticate(Authentication authentication) throws AuthenticationException;
- 参数:
Authentication对象,包含用户提供的凭证信息,例如用户名和密码。 - 返回值:如果认证成功,返回一个经过认证的 
Authentication对象,表示用户身份已验证;如果失败,则抛出AuthenticationException异常。 
简单来说,AuthenticationManager 的职责是对用户提交的凭证进行验证,确保用户身份合法。
2. AuthenticationManager的主要实现:ProviderManager
在Spring Security中,AuthenticationManager 最常见的实现类是 ProviderManager。ProviderManager 通过协调一组 AuthenticationProvider 来完成认证工作。
AuthenticationProvider接口AuthenticationProvider是另一个关键接口,定义了两个主要方法:Authentication authenticate(Authentication authentication):执行具体的认证逻辑。boolean supports(Class<?> authentication):判断该 provider 是否支持当前传入的Authentication类型。
ProviderManager的工作机制ProviderManager内部维护了一个AuthenticationProvider列表。当它的authenticate方法被调用时:- 遍历 
AuthenticationProvider列表。 - 找到第一个支持当前 
Authentication类型的AuthenticationProvider(通过supports方法判断)。 - 调用该 
AuthenticationProvider的authenticate方法进行认证。 - 如果认证成功,返回一个已认证的 
Authentication对象;如果失败,抛出异常。 
- 遍历 
 
这种设计使得 AuthenticationManager 具有很高的灵活性,可以支持多种认证方式(例如用户名/密码认证、OAuth2、LDAP 等),只需配置不同的 AuthenticationProvider 即可。
3. 常用的AuthenticationProvider:DaoAuthenticationProvider
在实际应用中,一个常用的 AuthenticationProvider 实现是 DaoAuthenticationProvider。它主要用于基于数据库的用户名/密码认证,工作流程如下:
- 加载用户信息:通过 
UserDetailsService从数据库中获取用户信息(UserDetails对象)。 - 密码验证:将用户提交的密码与数据库中的密码进行比对(通常结合密码加密器,如 
BCryptPasswordEncoder)。 - 返回结果:如果验证通过,返回一个认证成功的 
Authentication对象;否则抛出异常。 
开发者可以通过自定义 UserDetailsService 来指定如何从数据源加载用户信息,从而实现个性化的认证逻辑。
4. 其他AuthenticationManager实现
除了 ProviderManager,Spring Security 还提供了一些其他的 AuthenticationManager 实现,例如:
JaasAuthenticationManager:基于 Java Authentication and Authorization Service (JAAS) 进行认证,适用于需要集成 JAAS 的场景。- 自定义实现:开发者可以实现 
AuthenticationManager接口,创建完全定制化的认证逻辑。 
5. AuthenticationManager在认证流程中的角色
AuthenticationManager 是 Spring Security 认证流程的核心协调者。它的典型使用场景如下:
- 用户提交登录请求(例如用户名和密码)。
 - Spring Security 创建一个未认证的 
Authentication对象(通常是UsernamePasswordAuthenticationToken)。 AuthenticationManager的authenticate方法被调用。ProviderManager委托合适的AuthenticationProvider处理认证。- 认证结果被返回并存储到安全上下文中(
SecurityContext),供后续授权使用。 
6. 总结
AuthenticationManager是 Spring Security 中负责用户认证的核心接口。ProviderManager是其主要实现,通过管理多个AuthenticationProvider来支持不同的认证方式。DaoAuthenticationProvider是一个常用的 provider,适合基于数据库的用户名/密码认证。- 通过灵活的设计,
AuthenticationManager能够轻松扩展,支持各种认证场景。 
理解 AuthenticationManager 的工作原理,有助于开发者更好地配置和定制 Spring Security 的认证流程,从而满足不同的安全需求。
二、Authentication接口
在 Spring Security 中,Authentication 是一个核心接口,用于表示用户的认证信息。它不仅包含用户提交的凭证(例如用户名和密码),还包含认证成功后的用户权限等信息。下面我们将详细讲解 Authentication 接口及其在 Spring Security 认证过程中的作用。
1. Authentication 接口简介
Authentication 接口定义了用户认证信息的主要内容,通过以下方法提供访问:
getAuthorities():返回用户拥有的权限集合(例如角色或权限列表),类型为Collection<? extends GrantedAuthority>。getCredentials():返回用户的凭证,通常是密码、证书等。getDetails():返回用户的额外信息,例如 IP 地址或会话 ID。getPrincipal():返回用户的主体信息,通常是用户名或UserDetails对象。isAuthenticated():返回一个布尔值,表示用户是否已通过认证。setAuthenticated(boolean):设置认证状态,通常由 Spring Security 框架内部调用。
这些方法使得 Authentication 成为一个灵活的载体,能够同时表示认证请求和认证结果。
2. Authentication 的两种状态
Authentication 对象在认证过程中有两种主要状态:
未认证状态:
- 当用户提交凭证(例如用户名和密码)时,Spring Security 会创建一个未认证的 
Authentication对象。 - 此时,
isAuthenticated()返回false,表示凭证尚未被验证。 - 例如,一个 
UsernamePasswordAuthenticationToken对象会被初始化为包含用户名和密码,但认证状态为未完成。 
- 当用户提交凭证(例如用户名和密码)时,Spring Security 会创建一个未认证的 
 已认证状态:
- 认证成功后,Spring Security 会生成一个新的 
Authentication对象。 - 这个对象包含用户的权限(
authorities)、主体(principal)等信息,且isAuthenticated()返回true。 - 例如,认证后的 
UsernamePasswordAuthenticationToken会包含UserDetails对象和权限列表。 
- 认证成功后,Spring Security 会生成一个新的 
 
3. Authentication 的常见实现类
Spring Security 提供了多种 Authentication 接口的实现类,以支持不同的认证场景:
UsernamePasswordAuthenticationToken:- 最常用的实现,适用于基于用户名和密码的认证。
 - 未认证时包含用户名和密码,认证成功后包含 
UserDetails和权限。 
AnonymousAuthenticationToken:- 表示匿名用户,通常用于未登录用户的访问。
 
RememberMeAuthenticationToken:- 用于“记住我”功能,支持用户在关闭浏览器后仍保持登录状态。
 
JwtAuthenticationToken:- 用于基于 JWT(JSON Web Token)的认证,常见于 OAuth2 场景。
 
4. Authentication 在认证流程中的作用
Authentication 在 Spring Security 的认证流程中起着关键作用,以下是其工作流程:
用户提交凭证:
- 用户通过登录表单输入用户名和密码。
 - Spring Security 创建一个未认证的 
UsernamePasswordAuthenticationToken对象,封装用户名和密码。 
认证请求:
AuthenticationManager的authenticate方法接收这个未认证的Authentication对象。- 具体的 
AuthenticationProvider(例如DaoAuthenticationProvider)负责验证凭证,比如通过数据库检查用户名和密码是否匹配。 
认证成功:
- 如果凭证有效,
AuthenticationProvider返回一个已认证的Authentication对象。 - 这个对象包含用户的权限、主体等信息,且认证状态为 
true。 
- 如果凭证有效,
 存储到安全上下文:
- 认证成功后,
Authentication对象被存储到SecurityContextHolder中,供后续的授权和访问控制使用。 
- 认证成功后,
 
5. Authentication 使用示例
在大多数情况下,开发者无需直接操作 Authentication 对象,因为 Spring Security 的过滤器和配置会自动完成认证流程。但在某些自定义场景中,可能需要手动创建或访问 Authentication 对象。
示例 1:手动创建未认证的 Authentication 对象
Authentication authentication = new UsernamePasswordAuthenticationToken("username", "password");
这个对象可以传递给 AuthenticationManager 进行认证。
示例 2:获取当前用户的认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.isAuthenticated()) {String username = authentication.getName(); // 获取用户名Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); // 获取权限// 处理用户信息}
6. 总结
Authentication是 Spring Security 中表示用户认证信息的核心接口,包含凭证、权限、主体等内容。- 它在认证前后有未认证和已认证两种状态,分别对应认证请求和认证结果。
 - Spring Security 提供了多种实现类(如 
UsernamePasswordAuthenticationToken),支持不同的认证方式。 - 在认证流程中,
Authentication对象从用户提交凭证开始,经过验证后存储到安全上下文,用于后续的权限控制。 
理解 Authentication 的作用和工作机制,可以帮助开发者更好地掌握 Spring Security 的认证流程,从而实现安全、高效的用户认证功能。
三、SecurityContextHolder
Spring Security 中的 SecurityContextHolder 详解
SecurityContextHolder 是 Spring Security 中最核心的类之一,它用于存储和获取当前线程的安全上下文(SecurityContext),其中包含了当前用户的 身份认证信息(Authentication)。
1.SecurityContextHolder 作用
- 存储认证信息:
SecurityContextHolder持有SecurityContext,SecurityContext内部存储着Authentication(当前用户身份)。 - 全局访问认证信息:无论在 Controller、Service 还是其他组件,都可以通过 
SecurityContextHolder.getContext()访问当前认证信息。 - 支持不同的存储策略:默认使用 
ThreadLocal,即 线程级别存储,保证当前请求线程中的身份信息不会被其他线程访问。 
2. SecurityContextHolder 的结构
核心组成
public class SecurityContextHolder {private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();public static SecurityContext getContext() {SecurityContext ctx = contextHolder.get();if (ctx == null) {ctx = createEmptyContext();contextHolder.set(ctx);}return ctx;}public static void setContext(SecurityContext context) {contextHolder.set(context);}public static void clearContext() {contextHolder.remove();}}
getContext():获取当前线程的安全上下文,默认使用ThreadLocal进行存储。setContext(SecurityContext context):手动设置安全上下文。clearContext():清除当前线程的安全上下文,避免线程复用时信息泄漏。
3. SecurityContext 与 Authentication
SecurityContext作用
SecurityContext 是 SecurityContextHolder 内部存储的对象,用于持有 Authentication 信息:
public interface SecurityContext extends Serializable {Authentication getAuthentication();void setAuthentication(Authentication authentication);}
getAuthentication():获取当前认证的Authentication。setAuthentication(Authentication authentication):手动设置认证信息。
示例:获取当前用户身份
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String username = authentication.getName();Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
4. SecurityContextHolder 认证流程
默认认证流程
- 用户提交 用户名+密码 进行登录。
 AuthenticationManager.authenticate()认证用户。- 认证成功后,Spring Security 将 
Authentication存入SecurityContextHolder:SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
 - 后续请求时,可以通过 
SecurityContextHolder获取当前用户信息。 
5. SecurityContextHolder 的存储策略
Spring Security 默认使用 ThreadLocal 存储 SecurityContext,但也支持其他存储策略:
| 策略 | 描述 | 
|---|---|
MODE_THREADLOCAL(默认) | 
使用 ThreadLocal,每个线程独立存储安全上下文 | 
MODE_INHERITABLETHREADLOCAL | 
允许子线程继承 SecurityContext | 
MODE_GLOBAL | 
适用于无状态应用,所有线程共享同一个 SecurityContext | 
更改存储策略
在 Spring Security 启动前 设置:
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
6. SecurityContextHolder 的常见使用场景
6.1 在 Controller 获取当前用户
@RestController@RequestMapping("/user")public class UserController {@GetMapping("/me")public ResponseEntity<String> getCurrentUser() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();return ResponseEntity.ok("Current User: " + authentication.getName());}}
6.2 在 Service 层获取当前用户
@Servicepublic class UserService {public String getCurrentUsername() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.isAuthenticated()) {return authentication.getName();}return "Anonymous";}}
6.3 在 Spring Security 过滤器中手动设置 SecurityContext
如果使用 JWT 认证,我们通常需要手动设置 SecurityContextHolder:
public class JwtAuthenticationFilter extends OncePerRequestFilter {private final JwtService jwtService;private final UserDetailsService userDetailsService;public JwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) {this.jwtService = jwtService;this.userDetailsService = userDetailsService;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {String token = extractToken(request);if (token != null && jwtService.validateToken(token)) {String username = jwtService.getUsernameFromToken(token);UserDetails userDetails = userDetailsService.loadUserByUsername(username);Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);}chain.doFilter(request, response);}}
- 从 HTTP 头解析 JWT 令牌。
 - 验证令牌 并解析用户信息。
 - 构造 
Authentication并存入SecurityContextHolder。 
7. 线程安全问题
7.1 ThreadLocal 问题
SecurityContextHolder 默认使用 ThreadLocal,但在多线程环境(如异步任务)中,认证信息可能无法正确传递:
@Asyncpublic void someAsyncTask() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();System.out.println(authentication.getName()); // 可能为 null}
解决方案
使用 MODE_INHERITABLETHREADLOCAL:
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
或者手动传递 SecurityContext:
@Asyncpublic void someAsyncTask(SecurityContext securityContext) {SecurityContextHolder.setContext(securityContext);Authentication authentication = SecurityContextHolder.getContext().getAuthentication();System.out.println(authentication.getName());}
8. 总结
SecurityContextHolder是 Spring Security 认证信息的全局存储容器,默认使用ThreadLocal进行存储。SecurityContext内部存储Authentication,用于保存当前用户身份信息。- 可以在 
Controller、Service或Filter中访问当前用户:SecurityContextHolder.getContext().getAuthentication();
 - 在 JWT 或 SSO 认证场景,需要手动设置 
SecurityContextHolder。 - 多线程环境下需小心 
ThreadLocal,可以使用MODE_INHERITABLETHREADLOCAL或手动传递SecurityContext。 
SecurityContextHolder 在整个 Spring Security 体系中扮演着 身份认证信息存储 的关键角色,确保应用能够安全、高效地进行身份认证管理。


@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN") // 需要 ADMIN 角色.antMatchers("/user/**").hasAnyRole("USER", "ADMIN") // 需要 USER 或 ADMIN 角色.anyRequest().authenticated(); // 其他请求需要认证}}
在上述配置中,hasRole(“ADMIN”) 和 hasAnyRole(“USER”, “ADMIN”) 会被转换为 ConfigAttribute 对象。
