
1. JWT认证机制深度解析第一次接触JWT是在2016年做微服务架构改造时当时被它简洁的认证流程所吸引。相比传统的Session认证JWT最让我惊艳的是它的无状态特性——服务端不需要存储任何会话信息所有的认证数据都通过Token本身携带。这种设计完美契合了分布式系统的需求。JWT的全称是JSON Web Token它由三部分组成用点号(.)连接Header头部包含令牌类型和签名算法Payload负载存放实际传递的数据Signature签名对前两部分的加密签名一个典型的JWT看起来像这样eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c重要提示千万不要在JWT的Payload中存放敏感信息如密码等因为Payload只是Base64编码而非加密任何人都可以解码查看内容。2. Java 5环境下实现JWT认证虽然Java 5已经是很老的版本了(2004年发布)但在一些遗留系统中仍然有应用需求。以下是适配Java 5的JWT实现方案2.1 依赖库选择由于Java 5不支持很多现代加密库我们需要使用兼容性较好的实现dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version3.3.0/version !-- 最后一个支持Java 5的版本 -- /dependency如果无法使用Maven可以直接下载jar包jjwt-api-0.10.5.jarjjwt-impl-0.10.5.jarjjwt-jackson-0.10.5.jar2.2 核心工具类实现import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; public class JwtUtil { private static final String SECRET your-256-bit-secret; private static final Algorithm ALGORITHM Algorithm.HMAC256(SECRET); // 生成Token public static String createToken(String userId, long expireMillis) { return JWT.create() .withSubject(userId) .withExpiresAt(new Date(System.currentTimeMillis() expireMillis)) .sign(ALGORITHM); } // 验证Token public static boolean verifyToken(String token) { try { JWT.require(ALGORITHM).build().verify(token); return true; } catch (JWTVerificationException e) { return false; } } // 解析用户ID public static String getUserId(String token) { DecodedJWT jwt JWT.decode(token); return jwt.getSubject(); } }2.3 Java 5的特殊处理由于Java 5缺少一些现代API需要注意Base64编码需要使用第三方库如Apache Commons Codec日期处理建议使用java.util.Calendar而非新的时间API集合类使用需要手动同步没有并发安全集合3. JWT认证实战应用3.1 与Servlet集成在Java 5时代我们通常使用Servlet Filter实现认证拦截public class JwtFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) res; String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } token token.substring(7); // 去掉Bearer if (!JwtUtil.verifyToken(token)) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } String userId JwtUtil.getUserId(token); request.setAttribute(userId, userId); chain.doFilter(request, response); } }在web.xml中配置filter filter-namejwtFilter/filter-name filter-classcom.example.JwtFilter/filter-class /filter filter-mapping filter-namejwtFilter/filter-name url-pattern/api/*/url-pattern /filter-mapping3.2 性能优化技巧在Java 5环境下性能优化尤为重要使用静态Algorithm实例避免重复创建缓存验证结果但要注意Token过期时间使用StringBuilder处理字符串拼接对频繁调用的方法添加synchronized关键字4. 安全增强方案4.1 防止重放攻击在Java 5中实现Nonce校验public class NonceUtil { private static final MapString, Long NONCE_CACHE Collections.synchronizedMap(new HashMapString, Long()); public static boolean checkNonce(String nonce, long timestamp) { // 时间戳有效期5分钟 if (System.currentTimeMillis() - timestamp 300000) { return false; } if (NONCE_CACHE.containsKey(nonce)) { return false; } NONCE_CACHE.put(nonce, timestamp); return true; } }在Token生成时加入nonce和timestampString nonce UUID.randomUUID().toString(); long timestamp System.currentTimeMillis(); String token JWT.create() .withSubject(userId) .withClaim(nonce, nonce) .withClaim(timestamp, timestamp) // ...其他声明 .sign(ALGORITHM);4.2 Token刷新机制实现简单的双Token机制// 生成访问Token(短效) String accessToken JwtUtil.createToken(userId, 30 * 60 * 1000); // 30分钟 // 生成刷新Token(长效) String refreshToken JwtUtil.createToken(userId, 7 * 24 * 60 * 60 * 1000); // 7天刷新接口实现public void refreshToken(HttpServletRequest req, HttpServletResponse resp) { String refreshToken req.getHeader(Refresh-Token); if (!JwtUtil.verifyToken(refreshToken)) { resp.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } String userId JwtUtil.getUserId(refreshToken); String newAccessToken JwtUtil.createToken(userId, 30 * 60 * 1000); resp.setHeader(Authorization, Bearer newAccessToken); }5. 常见问题排查5.1 签名验证失败可能原因密钥不匹配 - 确保生成和验证使用相同密钥Token过期 - 检查exp声明算法不匹配 - 确保Header中的alg与验证时一致调试方法try { DecodedJWT jwt JWT.decode(token); System.out.println(Algorithm: jwt.getAlgorithm()); System.out.println(Expires at: jwt.getExpiresAt()); // 其他诊断信息... } catch (Exception e) { e.printStackTrace(); }5.2 Java 5特有问题Unsupported major.minor version错误确保编译和运行使用相同Java版本在Eclipse中设置Java Compiler兼容性缺少HMAC256算法安装JCE Unlimited Strength策略文件或使用更简单的算法如HS256内存泄漏注意及时清理静态Map中的过期Token使用WeakHashMap替代普通HashMap6. 升级建议虽然本文介绍了Java 5下的实现方案但强烈建议升级到至少Java 8因为更好的安全性支持更丰富的加密算法性能大幅提升现代JWT库的更好支持迁移到Java 8的改进点使用java.time API处理日期改用Lambda简化代码使用新的并发集合类支持更安全的加密算法对于必须使用Java 5的场景建议将认证服务独立部署使用反向代理处理HTTPS定期更换加密密钥实现严格的输入验证在老旧系统中实施JWT认证确实会遇到各种挑战但通过合理的架构设计和安全措施仍然可以构建可靠的认证系统。我在多个Java 5遗留系统改造项目中实践过这套方案最关键的是要理解JWT的核心原理然后根据具体环境做适应性调整。