本文主要是介绍JWT + 拦截器实现无状态登录系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话...
✅ 引言
在现代 Web 开发中,传统的 Session 认证方式在分布式、微服务架构下面临挑战:
JWT(jsON Web Token) 提供了一种无状态的解决方案:用户登录后,服务器返回一个 Token,后续请求携带该 Token 即可完成身份验证,无需服务器存储会话信息。
本文将结合 Spring Boot 拦截器,手把手实现一个完整的 JWT 无状态登录系统。
一、JWT 是什么?
JWT 是一个开放标准(RFC 7519),用于在各方之间安全地传输信息。
一个 JWT 通常由三部分组成,用 . 分隔:
xxxxx.yyyyy.zzzzz
- Header:令牌类型和签名算法
- Payload:存放用户信息(如用户 ID、角色、过期时间等)
- Signature:签名,用于验证 Token 是否被篡改
✅ 优点:自包含、可扩展、跨语言、China编程无状态。
二、技术选型
三、项目结构
src/ ├── main/ │ ├── Java/ │ │ └── com/example/jwtlogin/ │ │ ├── config/ android→ 配置类 │ │ ├── interceptor/ → 拦截器 │ │ ├── util/ → 工具类 │ │ ├── controller/ → 控制器 │ │ └── entity/ → 实体类
四、核心代码实现
4.1 添加依赖(pom.xml)
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>⚠️ 注意:JJWT 0.11+ 版本拆分了模块,需引入三个依赖。
4.2 JWT 工具类
@Component
public class JwtUtil {
// 密钥(应放在配置文件中)
private static final String SECRET = "your-256-bit-secret-your-256-bit-secret";
// 过期时间:24小时
private static final long EXPIRATION = 1000 * 60 * 60 * 24;
/**
* 生成 Token
*/
public String generateToken(String username, Long userId, String role) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("role", role);
return Jwts.builder()
.setSubject(username)
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
/**
* 解析 Token
*/
public Claims parseToken(String token) {
try {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
throw new RuntimeException("Token 已过期");
} catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
throw new RuntimeException("Token 无效");
}
}
/**
* 验证 Token 是否有效
*/
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}4.3 登录控制器
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private JwtUtil jwtUtil;
/**
* 用户登录
*/
@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(@RequestBody LoginRequest request) {
// 这里应调用 UserService 验证用户名密码
// 为简化,假设用户名密码正确
if ("admin".equals(request.getUsername()) && "123456".equals(request.getPassword())) {
String token = jwtUtil.generateToken(request.getUsername(), 1L, "ADMIN");
Map<String, Object> result = new HashMap<>();
result.put("token", token);
result.put("username", request.getUsername());
result.put("role", "ADMIN");
return ResponseEntity.ok(result);
} else {
return ResponseEntity.status(401).body(Map.of("msg", "用户名或密码错误"));
}
}
/**
* 用户登出(前端清空 Token 即可)
*/
@PostMapping("/logout")
public ResponseEntity<String> logout() {
return ResponseEntity.ok("登出成功");
}
}
// 登录请求 DTO
class LoginRequest {
private String username;
private String password;
// getter & setter
}4.4 JWT 拦截器
@Component
public class JwtIhttp://www.chinasem.cnnterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
private static final String AUTH_HEADER = "Authorization";
private static final String TOKEN_PREFIX = "Bearer ";
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 1. 放行 OPTIONS 请求(预检请求)
if ("OPTIONS".equalsIgnoreCase(request.getMethjavascriptod())) {
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
// 2. 放行登录接口
String requestURI = request.getRequestURI();
if ("/auth/login".equals(requestURI) || "/auth/logout".equals(requestURI)) {
return true;
}
// 3. 获取并验证 Token
String token = request.getHeader(AUTH_HEADER);
if (token == null || !token.startsWith(TOKEN_PREFIX)) {
response.setStatus(401);
response.getWriter().write("{\"code\":401,\"msg\":\"缺少 Token\"}");
return false;
}
token = token.substring(TOKEN_PREFIX.length());
try {
Claims claims = jwtUtil.parseToken(token);
// 将用户信息存入 request,供后续 Controller 使用
request.setAttribute("currentUser", claims.getSubject());
request.setAttribute("userId", claims.get("userId"));
request.setAttribute("role", claims.get("role"));
return true;
} catch (RuntimeException e) {
response.setStatus(401);
response.getWriter().write("{\"code\":401,\"msg\":\"" + e.getMessage() + "\"}");
return false;
}
}
}4.5 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/api/**") // 保护所有 API 接口
.excludePathPatterns("/auth/**", "/public/**"); // 放行认证和公共接口
}
}4.6 测试受保护的接口
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/profile")
public Map<String, Object> getProfile(HttpServletRequest request) {
Map<String, Object> profile = new HashMap<>();
profile.put("username", request.getAttribute("currentUser"));
profile.put("userId", request.getAttribute("userId"));
profile.put("role", request.getAttribute("role"));
profile.put("email", "admin@example.com");
return profile;
}
@GetMapping("/admin/data")
public String adminData(HttpServletRequest request) {
String role = (String) request.getAttribute("role");
if ("ADMIN".equals(role)) {
return "敏感数据:只有管理员可见";
} else {
return "权限不足";
}
}
}五、测试流程
1. 登录获取 Token
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'响应:
{
"token": "eyJhbGciOiJIUzI1NiJ9.xxxxx.yyyyy",
"username": "admin",
"role": "ADMIN"
}2. 携带 Token 访问受保护接口
curl http://localhost:8080/api/profile \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxx.yyyyy"响应:
{
"username": "admin",
"userId": 1,
"role": "ADMIN",
"email": "admin@example.com"
}3. 不带 Token 访问 → 401 未授权
六、生产环境优化建议
| 优化点 | 说明 |
|---|---|
| 密钥安全 | 将 SECRET 放在 application.yml 或环境变量中,不要硬编码 |
| Token 刷新 | 实现 Refresh Token 机制,避免频繁登录 |
| Token 黑名单 | 使用 Redis 记录已注销的 Token,防止被盗用 |
| 自定义注解 | 使用 @RequireAuth(role="ADMIN") 简化权限控制 |
| 日志监控 | 记录 Token 解析失败日志,便于排查问题 |
✅ 总结
| 步骤 | 说明 |
|---|---|
| 1. 用户登录 | 验证账号密码,生成 JWT |
| 2. 客户端存储 | 将 Token 存入 l编程China编程ocalStorage 或 Cookie |
| 3. 携带请求 | 每次请求在 Authorization 头中携带 Bearer Token |
| 4. 拦截器验证 | 解析 Token,校验签名和过期时间 |
| 5. 放行或拒绝 | 验证通过则放行,否则返回 401 |
无状态登录的核心:服务器不保存会话状态,所有信息都封装在 Token 中,由客户端负责携带和管理。
推荐
- JWT 官网
到此这篇关于JWT + 拦截器实现无状态登录系统的文章就介绍到这了,更多相关JWT 拦截器无状态登录内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于JWT + 拦截器实现无状态登录系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!