Spring Security
完整认证与授权流程
请求进入
↓
CorsFilter (处理跨域)
↓
LogoutFilter (处理退出请求)
↓
JwtAuthenticationTokenFilter (手动实现:解析并验证 Token)
↓
├── Token 有效 ➔ 构建 Authentication 对象并存入 SecurityContextHolder
└── Token 无效或过期 ➔ 抛出异常,由 ExceptionTranslationFilter 处理
↓
ExceptionTranslationFilter (处理认证/授权异常)
↓
FilterSecurityInterceptor (自动配置:执行最终权限校验)
↓
├── 权限足够 ➔ 访问目标资源
└── 权限不足 ➔ 抛出 AccessDeniedException,由 ExceptionTranslationFilter 处理
总结
- 无需手动编写:
FilterSecurityInterceptor由 Spring Security 自动创建,开发者只需通过.authorizeHttpRequests()配置权限规则。 - 需手动实现:JWT 解析过滤器(
JwtAuthenticationTokenFilter)、Token 管理逻辑(TokenService)及异常处理器(AuthenticationEntryPoint)。 - Token 过期处理:在
TokenService中校验exp时间,抛出异常后由全局异常处理器响应 401,客户端需重新登录或刷新 Token。
Spring Security 的核心原理
以下是 Spring Security 的核心原理与使用方式的逐步解析:
1. 核心流程:过滤器链的拦截机制
Spring Security 的核心是一系列过滤器链,每个过滤器负责不同的安全任务:
- 请求拦截:所有 HTTP 请求会依次通过过滤器链中的每个过滤器。
- 示例:访问
/login时,UsernamePasswordAuthenticationFilter会拦截表单提交的用户名密码。
- 示例:访问
- 关键过滤器:
- SecurityContextPersistenceFilter:从 Session 或请求头中加载用户认证信息(如 Token)。
- UsernamePasswordAuthenticationFilter:处理表单登录请求,生成未认证的
Authentication对象。 - FilterSecurityInterceptor:最终检查用户是否有权限访问资源。
- 异常处理:
ExceptionTranslationFilter捕获认证或权限异常,返回 401 或 403 错误。
2. 认证流程详解
认证(Authentication)是验证用户身份的过程:
- 用户提交凭证:通过表单、Token(如 JWT)等方式提交用户名、密码或其他凭证。
- 创建 Authentication 对象:
// 示例:表单登录创建 Authentication 对象 Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password); - 调用 AuthenticationManager:
AuthenticationManager是认证入口,会调用具体的AuthenticationProvider(如DaoAuthenticationProvider)进行验证。
- 加载用户信息:
UserDetailsService从数据库或缓存中查询用户信息(如密码、权限),返回UserDetails对象。
- 密码校验:
- 使用
PasswordEncoder(如 BCrypt)对比用户输入的密码和存储的加密密码是否一致。
- 使用
- 存储认证信息:
- 认证成功后,
Authentication对象存入SecurityContextHolder,供后续授权使用。
- 认证成功后,
3. 授权流程(Authorization)
授权是检查用户是否有权访问资源的过程:
- 权限定义:
- 基于 URL:在配置类中定义路径规则,如
.antMatchers("/admin/**").hasRole("ADMIN")。 - 基于注解:在方法上使用
@PreAuthorize("hasAuthority('DELETE_USER')")动态校验权限。
- 基于 URL:在配置类中定义路径规则,如
- 权限决策:
AccessDecisionManager调用投票器(如RoleVoter),根据用户的权限集合(GrantedAuthority)决定是否放行。
- 异常处理:
- 若权限不足,抛出
AccessDeniedException,由ExceptionTranslationFilter处理为 403 响应。
- 若权限不足,抛出
4. RBAC 模型的实现
RBAC(基于角色的访问控制)是常用的权限模型:
- 数据库设计:
- 用户表(User)、角色表(Role)、权限表(Permission),通过关联表(user_role、role_permission)建立多对多关系。
- 权限加载:
- 自定义
UserDetailsService,从数据库查询用户的角色和权限,封装到UserDetails中。
// 示例:从数据库加载用户权限 UserDetails user = userRepository.findByUsername(username); user.getRoles().forEach(role -> authorities.add(new SimpleGrantedAuthority(role.getName()))); - 自定义
- 动态控制:
- 在方法上使用
@PreAuthorize注解,结合 SpEL 表达式动态校验权限:
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id") public void updateUser(Long userId) { ... } - 在方法上使用
5. 扩展功能示例
(1) 自定义认证方式(如手机验证码登录)
- 自定义 UserDetails:
public class PhoneUserDetails implements UserDetails { ... } - 自定义 Authentication 对象:
public class PhoneAuthenticationToken extends AbstractAuthenticationToken { ... } - 自定义 AuthenticationProvider:
public class PhoneAuthenticationProvider implements AuthenticationProvider { public Authentication authenticate(Authentication auth) { String phone = auth.getPrincipal().toString(); String code = auth.getCredentials().toString(); // 校验验证码并加载用户信息 } } - 注册自定义 Provider:
@Bean public AuthenticationProvider phoneAuthenticationProvider() { return new PhoneAuthenticationProvider(); }
(2) JWT 集成
- 生成 Token:
String token = Jwts.builder().setSubject(username).signWith(secretKey).compact(); - 解析 Token:
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody(); - 自定义 JWT 过滤器:
public class JwtFilter extends OncePerRequestFilter { protected void doFilterInternal(...) { String token = request.getHeader("Authorization"); // 解析 Token 并设置到 SecurityContext } }
6. 核心组件总结
| 组件 | 作用 |
|---|---|
SecurityContextHolder |
存储当前用户的认证信息(如 Token、角色) |
AuthenticationManager |
协调认证流程,调用具体的 AuthenticationProvider |
UserDetailsService |
从数据库或其他数据源加载用户信息 |
AccessDecisionManager |
根据用户权限和资源规则决定是否授权 |
FilterChainProxy |
管理所有过滤器链,处理 HTTP 请求的安全逻辑 |
7. 适用场景与最佳实践
- 适用场景:
- 企业级系统(如 OA、CRM)的权限分层管理。
- 微服务架构中的无状态认证(如 JWT + OAuth2)。
- 最佳实践:
- 使用
BCryptPasswordEncoder加密密码。 - 通过
@EnableGlobalMethodSecurity启用方法级权限控制。 - 定期审计权限分配,避免过度授权。
- 使用
通过以上步骤,可以逐步理解 Spring Security 的工作原理。如需更深入的代码示例或调试技巧,可参考各章节引用的源码和文档链接。

浙公网安备 33010602011771号