- 一、 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 层获取当前用户
@Service
public 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;
}
@Override
protected 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,但在多线程环境(如异步任务)中,认证信息可能无法正确传递:
@Async
public void someAsyncTask() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println(authentication.getName()); // 可能为 null
}
解决方案
使用 MODE_INHERITABLETHREADLOCAL:
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
或者手动传递 SecurityContext:
@Async
public 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
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected 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 对象。
 我的书签
 我的书签
                                 添加书签
 添加书签 移除书签
 移除书签