视频看了几百小时还迷糊?关注我,几分钟让你秒懂!
你是否经常听到这些词:
- “我们用 JWT 做登录认证”
- “前端把 token 放在 Authorization 头里”
- “JWT 无状态,适合分布式系统”
但你真的理解JWT 到底是什么?它怎么工作?和 Session 有什么区别?吗?
今天我们就用通俗语言 + 图解 + Spring Boot 代码实战,带你从零彻底搞懂 JWT!
🧩 一、一句话解释 JWT
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用间安全地传递“声明”(claims)的紧凑、自包含令牌。
简单说:JWT 就是一个加密的字符串,里面包含了用户身份信息,服务器不用查数据库就能验证你是谁!
🔍 二、JWT 长什么样?
一个典型的 JWT 看起来像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c它由三部分组成,用点.分隔:
| 部分 | 说明 | 是否可读 |
|---|---|---|
| Header(头部) | 算法 + 类型 | Base64 可解码 |
| Payload(载荷) | 用户数据(如 ID、角色、过期时间) | Base64 可解码 |
| Signature(签名) | 用于验证令牌是否被篡改 | 不可逆 |
✅注意:JWT 默认是 Base64 编码,不是加密!任何人都能解码看到内容!
🔒 安全靠的是签名(Signature),防止内容被篡改。
📦 三、三部分详解(附解码示例)
1. Header(头部)
{ "alg": "HS256", "typ": "JWT" }alg:签名算法(如 HS25 sH A256、RS256)typ:令牌类型,固定为 JWT
2. Payload(载荷)— 存放用户信息的地方!
{ "sub": "1234567890", // 主题(通常是用户ID) "name": "John Doe", "admin": true, "iat": 1516239022, // 签发时间(时间戳) "exp": 1516242622 // 过期时间(重要!) }⚠️不要在 Payload 里放敏感信息(如密码、手机号)!因为它是明文(Base64 可解码)!
3. Signature(签名)— 安全的核心!
服务器用密钥 + 算法对Header + Payload进行签名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secretKey )- 客户端无法伪造签名(不知道 secretKey);
- 服务器收到 JWT 后,会重新计算签名,比对是否一致;
- 只要 Payload 被改,签名就失效!
🔄 四、JWT 认证流程(图解)
✅ 整个过程服务器无需存储 session,真正“无状态”!
⚖️ 五、JWT vs Session(传统方案)
| 特性 | JWT | Session |
|---|---|---|
| 存储位置 | 客户端(LocalStorage/Cookie) | 服务端(内存/Redis) |
| 状态 | 无状态(Stateless) | 有状态(需维护 session) |
| 扩展性 | 天然支持分布式 | 需共享 session(如 Redis) |
| 安全性 | 依赖签名,防篡改 | 依赖 session ID 随机性 |
| 登出 | 难(需黑名单或短期过期) | 容易(删 session 即可) |
| 传输大小 | 较大(含用户数据) | 小(只传 session ID) |
💡JWT 适合:API 服务、微服务、移动端
💡Session 适合:传统 Web 应用、需要强登出控制的系统
💻 六、Spring Boot 实战:手写 JWT 登录
1. 添加依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </attribute> <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>2. JWT 工具类
@Component public class JwtUtil { private String secret = "MySecretKey123!@#"; // 生产环境应配置在 application.yml private long expiration = 86400000; // 24小时 public String generateToken(String userId) { return Jwts.builder() .setSubject(userId) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public String getUserIdFromToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody() .getSubject(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(secret).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } }3. 登录接口
@RestController public class AuthController { @Autowired private JwtUtil jwtUtil; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest req) { // 模拟验证账号密码(实际应查数据库) if ("admin".equals(req.getUsername()) && "123456".equals(req.getPassword())) { String token = jwtUtil.generateToken("10001"); return ResponseEntity.ok(Map.of("token", token)); } return ResponseEntity.status(401).body("账号或密码错误"); } }4. 拦截器:验证 JWT
public class JwtInterceptor implements HandlerInterceptor { @Autowired private JwtUtil jwtUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { String token = authHeader.substring(7); if (jwtUtil.validateToken(token)) { // 可将用户ID存入 ThreadLocal 或 Request Attribute return true; } } response.setStatus(401); return false; } }注册拦截器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JwtInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns("/login"); } }⚠️ 七、JWT 的致命缺点 & 注意事项
❌ 1.无法主动登出(除非用黑名单)
- JWT 一旦签发,在过期前一直有效;
- 即使用户点击“退出”,旧 token 仍可使用;
- 解决方案:
- 设置较短过期时间(如 15 分钟)+ 刷新令牌(Refresh Token);
- 维护一个 Redis 黑名单,记录已登出的 token。
❌ 2.Payload 不是加密的!
- 所有人都能解码看到内容;
- 永远不要放密码、身份证号等敏感信息!
❌ 3.密钥泄露 = 全盘崩溃
- 如果
secretKey泄露,攻击者可伪造任意用户 token; - 必须严格保管密钥!
✅ 最佳实践建议:
| 项目 | 建议 |
|---|---|
| 过期时间 | Access Token 15~30 分钟,配合 Refresh Token |
| 存储位置 | Web 用 HttpOnly Cookie(防 XSS),App 用安全存储 |
| 传输协议 | 必须 HTTPS |
| 密钥管理 | 用配置中心或环境变量,不要硬编码 |
🎯 总结
- JWT = Header + Payload + Signature
- 自包含、无状态、适合分布式
- 不是加密,而是签名防篡改
- 无法主动登出是最大短板
- Spring Boot 集成简单,但要注意安全细节
掌握 JWT,你就掌握了现代 API 认证的“通行证”!
视频看了几百小时还迷糊?关注我,几分钟让你秒懂!