🧭 一、前言
在现代前后端分离项目中,用户认证与权限控制是绕不开的话题。传统的基于 Session 的方案在微服务或跨域环境下显得笨重、不灵活。为了解决这些问题,我们采用 Spring Boot 3.x 搭配 Spring Security 6.x,通过 JWT 实现无状态的认证体系,并集成方法级权限注解、Token 自动续期与黑名单控制机制,构建一个安全、高效、可扩展的权限系统。
🛠 二、技术选型与项目结构
🔧 技术栈
- Spring Boot 3.x
- Spring Security 6.x
- JSON Web Token (jjwt)
- MyBatis + MySQL
- Redis(可选,用于 Token 黑名单)
📁 模块结构图
arduino
com.example.demo
├── config
│ └── SecurityConfig.java
├── controller
│ ├── AuthController.java
│ ├── DemoController.java
│ └── HelloController.java
├── entity
│ └── User.java
├── filter
│ └── JwtAuthenticationFilter.java
├── handler
│ ├── CustomAccessDeniedHandler.java
│ └── CustomLoginFailureHandler.java
├── mapper
│ └── UserMapper.java
├── point
│ └── CustomAuthenticationEntryPoint.java
├── service
│ └── MyUserDetailsService.java
├── utils
│ ├── JwtUtils.java
│ └── TokenBlacklist.java
└── SecurityDemoApplication.java
🔐 三、功能实现详解
3.1 用户登录与 Token 生成
用户通过 /auth/login
接口提交用户名密码,系统验证通过后,签发一个包含用户名和角色信息的 JWT:
scss
String token = Jwts.builder()
.setSubject(username)
.claim("role", role)
.setIssuedAt(now)
.setExpiration(expiry)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
前端需将该 Token 保存在本地(如 localStorage)并在后续请求中通过:
makefile
Authorization: Bearer xxxxxx
方式传递。
3.2 JWT 鉴权流程
JWT 鉴权通过 JwtAuthenticationFilter
实现,它继承 OncePerRequestFilter
,会在每次请求时验证 Token 的合法性,并将用户信息注入 Spring Security 上下文中:
ini
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
Claims claims = jwtUtils.parseToken(token);
// 验证通过后构建Authentication对象
UsernamePasswordAuthenticationToken authToken = ...
SecurityContextHolder.getContext().setAuthentication(authToken);
}
📝 流程图(可视化):
rust
前端 -> 登录接口 -> JWT签发 -> 保存Token
-> 带Token访问受保护接口 -> 过滤器校验 -> 放行或拒绝
3.3 方法级权限控制
Spring Security 支持使用 @PreAuthorize
注解实现方法级权限控制:
less
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin")
public String adminApi() {
return "管理员接口";
}
确保在 SecurityConfig
中启用:
css
@EnableMethodSecurity
3.4 Token 自动续期
每次成功访问受保护接口时,过滤器判断 Token 剩余时间是否低于阈值(如10分钟),如果是则自动签发新 Token 并通过响应头 X-Refresh-Token
返回:
ini
if (jwtUtils.shouldRefresh(claims.getExpiration())) {
String newToken = jwtUtils.generateToken(username, role);
response.setHeader("X-Refresh-Token", newToken);
}
📌 前端监听 X-Refresh-Token
并自动更新本地 Token。
3.5 Token 黑名单机制
用户退出登录时,将当前 Token 加入黑名单(可用 Redis 设置自动过期):
bash
redisTemplate.opsForValue().set("blacklist:" + token, "1", ttl, TimeUnit.MILLISECONDS);
在过滤器中拒绝黑名单内的 Token:
kotlin
if (redisTemplate.hasKey("blacklist:" + token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
✅ 黑名单使用 Redis TTL 自动过期,无需手动清理。
3.6 登录失败与未授权处理
自定义以下处理器统一返回 JSON 格式错误信息:
错误类型 | 处理器类 | HTTP 状态码 |
---|---|---|
未登录或无效 Token | CustomAuthenticationEntryPoint |
401 |
权限不足 | CustomAccessDeniedHandler |
403 |
登录失败(账号密码错误) | CustomLoginFailureHandler |
401 |
📌 示例返回 JSON:
css
{
"code": 401,
"message": "未登录或Token无效"
}
🧱 四、常见问题与解决方案
- ❓
AuthenticationManager
Bean 不存在?
✅ 手动构造并通过@Bean
注入 - ❓ JWT 密钥长度报错?
✅ 使用Keys.secretKeyFor(SignatureAlgorithm.HS256)
或 ≥32位字符串 - ❓ 本地和远程仓库历史不同?
✅git pull origin main --allow-unrelated-histories
📌 五、总结与扩展
本文完整实现了一个支持 JWT 无状态认证的权限系统,涵盖了登录认证、方法级授权、Token 自动续期、黑名单强退、异常统一处理等核心功能,具备良好的扩展性与实战价值。
📈 可扩展方向:
- OAuth2 第三方登录(如 Gitee / GitHub / 企业微信)
- 单点登录(SSO)
- 菜单/按钮级动态权限控制(RBAC 权限模型)
- 登录日志 / Token 活跃追踪 / 多终端限制
🧷 六、源码地址
👉 Gitee 地址:https://gitee.com/nidayeyo/spring-security-demo
如果你觉得本项目对你有帮助,欢迎 Star、Fork 并留言交流~ 🙌