Spring Boot项目中JWT Token可被解码的原因及安全优化疑问
关于Spring Boot中JWT Token的安全性疑问
我在Spring Boot项目中使用JWT Token,发现该Token可通过官方网站解码并还原所有数据,我的Token为:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTdGVlbERvYyIsImV4cCI6MTU1NzU2ODQ1MiwibG9naW5JZCI6ImFkbWluIiwidXNlcklkIjoyLCJlbWFpbElkIjoiZGhpcmVuZHJhQHRlc3QuY29tIn0.o4UKtwO22cAXLjIsUuDRgAQf7OjUbe_O9DbSGxPYgfQ
以下是我的实现代码:
public class JwtHelper { static String subject = "SteelDoc"; static String secret = "faa76006-6fef-413d-9cae-a05e63170cbf"; Integer userId = null; String loginId = ""; String emailId = ""; public String getUserName() { return loginId; } public void setUserName(String userName) { this.loginId = userName; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getToken(String loginId, Integer userId, String email) { try { Date d = new Date(); Date de = d; de.setYear(de.getYear() + 1); String jwt = Jwts.builder() .setSubject(subject) .setExpiration(de) .claim("loginId", loginId) .claim("userId", userId) .claim("emailId", email) .signWith(SignatureAlgorithm.HS256, secret.getBytes("UTF-8")) .compact(); return jwt; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } public boolean validateToken(String token) { String jwt = token; Jws<Claims> claims; try { claims = Jwts.parser() .setSigningKey(secret.getBytes("UTF-8")) .parseClaimsJws(jwt); userId = (Integer) claims.getBody().get("userId"); loginId = (String) claims.getBody().get("loginId"); emailId = (String) claims.getBody().get("emailId"); return true; } catch (ExpiredJwtException e) { e.printStackTrace(); } catch (UnsupportedJwtException e) { e.printStackTrace(); } catch (MalformedJwtException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return false; } }
我有三个技术疑问:
- 为何JWT Token会被轻易解码并还原数据?
- 若JWT存在此安全特性,为何仍被广泛使用?
- 有没有方法增强JWT Token的安全性?
解答
1. 为何JWT Token会被轻易解码并还原数据?
这其实是JWT的设计特性,而非漏洞。JWT由Header(头部)、Payload(载荷)、Signature(签名)三部分组成:
- Header和Payload都是用Base64URL编码的,这种编码只是把JSON格式转成适合HTTP传输的字符串,完全可逆、不是加密,所以任何人都能解码出里面的内容。
- JWT的安全核心在Signature部分:服务器用密钥对Header和Payload进行签名,后续验证时会重新计算签名并对比,只要密钥没泄露,篡改过的Token会因为签名不匹配被拒绝。
简单说,JWT不负责隐藏数据,只负责保证数据没被篡改。
2. 若JWT存在此安全特性,为何仍被广泛使用?
这种“可解码”的设计恰恰是JWT的优势,它能成为主流身份认证方案的原因包括:
- 无状态认证:服务器不用存储会话,靠密钥验证签名就能确认Token有效性,完美适配分布式、微服务架构,减少存储和同步成本。
- 跨平台兼容:Base64URL和JSON是通用格式,几乎所有编程语言都能轻松解析,方便在不同服务间传递身份信息。
- 性能优化:Payload可以携带常用的非敏感身份信息(比如用户ID、登录名),避免每次请求都查数据库,提升接口响应速度。
- 标准成熟:遵循RFC 7519规范,有大量第三方库支持,集成成本极低。
当然,这要求我们绝对不能在Payload里放敏感数据(比如密码、银行卡号),否则会被轻易泄露。
3. 有没有方法增强JWT Token的安全性?
结合你的代码,给你几个实用的优化方向:
- 剔除Payload中的敏感数据:只保留必要的非敏感标识(比如用户ID、登录名),这是最基础的安全原则。
- 更换签名算法:你当前用的HS256是对称加密,密钥必须严格保密,一旦泄露就会被伪造Token。分布式场景建议用RS256非对称算法——私钥在服务器端签名,公钥对外公开验证,即使公钥泄露也无法伪造Token。
- 缩短过期时间:你代码里设置1年过期太长了!建议改成15分钟到1小时,同时配合Refresh Token机制:用有效期更长的Refresh Token来获取新的访问Token,既保证安全,又不用频繁登录。
- HTTPS传输:一定要通过HTTPS传递JWT,避免Token在网络传输中被窃听。
- 密钥安全管理:不要像现在这样把密钥硬编码到代码里,应该用环境变量、配置中心或密钥管理服务存储,防止密钥泄露。
- 补充标准字段验证:在Payload中加入
iss(签发者)、aud(受众)字段,验证Token的来源和使用范围;加入jti(唯一标识),实现Token黑名单,在用户注销、改密码时失效指定Token。 - 修复过期时间代码:你当前的
Date de = d; de.setYear(de.getYear() + 1);是错误的(setYear已过时,还会修改原Date对象),建议用Java 8+的java.time包:
Instant now = Instant.now(); Instant expiration = now.plus(Duration.ofHours(1)); // 设置1小时过期
内容的提问来源于stack exchange,提问作者Dhirendra Sharma
相关产品推荐
相关产品推荐

