Spring Security 认证流程
Spring Security 认证流程是一个高度可扩展的链条式处理过程,下面是完整流程及各种自定义扩展点的详细说明:
Spring Security 认证完整流程
请求拦截
FilterChainProxy
委托给SecurityFilterChain
- 认证过滤器(如
UsernamePasswordAuthenticationFilter
)拦截请求
创建认证对象
- 从请求中提取凭证(用户名/密码、Token等)
- 创建未认证的
Authentication
对象(如UsernamePasswordAuthenticationToken
)
认证管理器处理
AuthenticationManager
(通常是ProviderManager
)接收认证请求- 委托给支持的
AuthenticationProvider
认证提供者处理
AuthenticationProvider.supports()
检查是否能处理该认证类型- 调用
authenticate()
执行核心认证逻辑
用户信息加载
- 通过
UserDetailsService.loadUserByUsername()
加载用户信息 - 返回
UserDetails
对象(包含用户名、密码、权限等)
- 通过
凭证验证
- 使用
PasswordEncoder.matches()
比对请求密码和存储密码 - 检查账户状态(启用/锁定/过期等)
- 使用
构建认证对象
- 创建完全认证的
Authentication
对象 - 包含用户信息、权限列表等
- 创建完全认证的
存储认证上下文
- 将认证对象存入
SecurityContextHolder
- 通过
SecurityContextRepository
持久化(如Session存储)
- 将认证对象存入
自定义扩展点详解
1. 自定义认证过滤器
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
String token = extractToken(request);
if (token != null) {
// 创建未认证Token
Authentication authRequest = new JwtAuthenticationToken(token);
// 触发认证流程
Authentication authResult = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authResult);
}
chain.doFilter(request, response);
}
}
使用场景:添加JWT、API Key等非标准认证方式
2. 自定义 AuthenticationProvider
public class FingerprintAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication auth) {
FingerprintAuthenticationToken token = (FingerprintAuthenticationToken) auth;
UserDetails user = fingerprintService.verify(token.getFingerprintData());
return new FingerprintAuthenticationToken(user, null, user.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return FingerprintAuthenticationToken.class.isAssignableFrom(authentication);
}
}
使用场景:生物识别、硬件设备认证等特殊认证方式
3. 自定义 UserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByUsernameOrEmailOrPhone(username);
return new CustomUserDetails(
user.getLoginId(),
user.getEncryptedPassword(),
getAuthorities(user.getRoles())
);
}
}
使用场景:多字段登录(用户名/手机/邮箱)、多数据源加载
4. 自定义 UserDetails
public class CustomUserDetails extends User {
private Long departmentId;
private String timezone;
public CustomUserDetails(String username, String password,
Collection<? extends GrantedAuthority> authorities,
Long departmentId) {
super(username, password, authorities);
this.departmentId = departmentId;
}
// 附加业务字段
}
使用场景:扩展用户属性(部门、时区等业务字段)
5. 自定义 PasswordEncoder
public class LegacyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return MyLegacySystem.encrypt(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String encrypted = MyLegacySystem.encrypt(rawPassword.toString());
return encrypted.equals(encodedPassword);
}
}
使用场景:兼容旧系统加密算法、多加密策略并存
6. 自定义 Authentication 对象
public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
private final OAuth2User principal;
private String accessToken;
public OAuth2AuthenticationToken(OAuth2User principal,
Collection<? extends GrantedAuthority> authorities,
String accessToken) {
super(authorities);
this.principal = principal;
this.accessToken = accessToken;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return accessToken;
}
@Override
public Object getPrincipal() {
return principal;
}
}
使用场景:携带额外认证信息(OAuth令牌、会话ID等)
7. 自定义认证事件处理
@Component
public class CustomAuthEventListener {
@EventListener
public void handleAuthenticationSuccess(AuthenticationSuccessEvent event) {
Authentication auth = event.getAuthentication();
auditService.logLoginSuccess(auth.getName());
}
@EventListener
public void handleAuthenticationFailure(AbstractAuthenticationFailureEvent event) {
String username = (String) event.getAuthentication().getPrincipal();
auditService.logLoginFailure(username, event.getException());
}
}
使用场景:审计日志、登录通知、锁定策略
8. 多认证方式共存配置
@Configuration
@EnableWebSecurity
public class MultiAuthConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationProvider daoAuthProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public AuthenticationProvider otpAuthProvider() {
return new OtpAuthenticationProvider();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(daoAuthProvider())
.authenticationProvider(otpAuthProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class)
.formLogin()
.and()
.addFilterAfter(new OtpVerificationFilter(), BasicAuthenticationFilter.class);
}
}
支持场景:
- 用户名密码 + 短信验证码
- JWT + 表单登录
- 主认证 + 二次认证(2FA)
高级自定义场景
场景1:动态权限加载
public class DynamicPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object target, Object permission) {
String username = auth.getName();
// 实时查询数据库获取最新权限
List<String> permissions = permissionService.getUserPermissions(username);
return permissions.contains(permission.toString());
}
}
场景2:认证失败自定义响应
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) {
response.setContentType("application/json");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("""
{
"error": "AUTH_REQUIRED",
"message": "请提供有效的认证凭证",
"login_url": "/api/v1/auth/login"
}
""");
}
}
场景3:分布式会话管理
@Bean
public SecurityContextRepository securityContextRepository() {
return new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(), // 请求级缓存
new RedisSecurityContextRepository(redisTemplate) // Redis持久化
);
}
最佳实践建议
优先使用组合而非继承
- 扩展
DaoAuthenticationProvider
而非实现底层接口 - 包装现有组件而非重写完整流程
- 扩展
保持认证与业务分离
- 认证组件不应包含业务逻辑
- 通过事件监听器处理业务副作用
分层自定义策略
HTTP层 → 认证过滤器 (处理协议) | V 认证管理层 → AuthenticationProvider (核心认证逻辑) | V 数据访问层 → UserDetailsService (加载数据)
测试策略
- 使用
@WithMockUser
测试权限 - 使用
SecurityTestExecutionListener
集成测试 - 模拟
AuthenticationManager
单元测试
- 使用
通过深度理解这些扩展点,可以灵活实现从简单的表单登录到复杂的多因素认证系统,满足各种安全需求。