JWT令牌安全实践详解

JWT令牌安全实践详解

一、JWT概述

JSON Web Token(JWT)是一种用于安全传输信息的开放标准(RFC 7519)。

1.1 JWT结构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        JWT Token                            │
├─────────────────────────────────────────────────────────────┤
│  Header.Payload.Signature                                   │
│     │        │           │                                  │
│     ▼        ▼           ▼                                  │
│  {"alg":"HS256",    {"sub":"123",    HMACSHA256(           │
│   "typ":"JWT"}       "name":"John",   base64Url(header).   │
│                      "exp":1704067200) + "." +              │
│                                            base64Url(payload),│
│                                            secret)          │
└─────────────────────────────────────────────────────────────┘

1.2 JWT组成部分

部分 说明 编码方式
Header 算法和类型 Base64Url
Payload 声明信息 Base64Url
Signature 签名 HMAC/RSA

二、JWT实现

2.1 创建JWT

java 复制代码
import io.jsonwebtoken.*;
import java.util.Date;

public class JwtUtil {
    
    private static final String SECRET_KEY = "your-256-bit-secret-key";
    private static final long EXPIRATION_TIME = 86400000; // 24小时
    
    public static String generateToken(String userId, String username) {
        return Jwts.builder()
                .setSubject(userId)
                .claim("username", username)
                .claim("role", "admin")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
}

2.2 验证JWT

java 复制代码
public static Claims validateToken(String token) {
    try {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    } catch (JwtException | IllegalArgumentException e) {
        throw new RuntimeException("Invalid token", e);
    }
}

2.3 刷新Token

java 复制代码
public static String refreshToken(String token) {
    Claims claims = validateToken(token);
    claims.setIssuedAt(new Date());
    claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
    
    return Jwts.builder()
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
            .compact();
}

三、JWT安全配置

3.1 密钥管理

java 复制代码
// 使用256位以上的密钥
private static final String SECRET_KEY = generateSecureKey();

private static String generateSecureKey() {
    SecureRandom random = new SecureRandom();
    byte[] key = new byte[32]; // 256 bits
    random.nextBytes(key);
    return Base64.getEncoder().encodeToString(key);
}

3.2 使用RSA非对称加密

java 复制代码
// 生成RSA密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();

// 使用私钥签名
String token = Jwts.builder()
        .setSubject("user123")
        .signWith(keyPair.getPrivate(), SignatureAlgorithm.RS256)
        .compact();

// 使用公钥验证
Claims claims = Jwts.parser()
        .setSigningKey(keyPair.getPublic())
        .parseClaimsJws(token)
        .getBody();

3.3 设置合理的过期时间

java 复制代码
// 访问Token:短过期时间
private static final long ACCESS_TOKEN_EXPIRE = 15 * 60 * 1000; // 15分钟

// 刷新Token:长过期时间
private static final long REFRESH_TOKEN_EXPIRE = 7 * 24 * 60 * 60 * 1000; // 7天

四、安全最佳实践

4.1 Token存储策略

存储位置 优点 缺点 适用场景
LocalStorage 方便访问 XSS风险 单页应用
SessionStorage 会话级存储 页面切换丢失 临时数据
HttpOnly Cookie XSS安全 CSRF风险 传统Web
Memory 最安全 页面刷新丢失 高安全场景

4.2 CSRF防护

java 复制代码
// Spring Security配置
http.csrf(csrf -> csrf
    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    .ignoringRequestMatchers("/api/**")
);

4.3 XSS防护

html 复制代码
<!-- 设置HttpOnly和Secure标志 -->
Set-Cookie: JWT=token; HttpOnly; Secure; SameSite=Strict

五、Token黑名单机制

5.1 基于Redis的黑名单

java 复制代码
public class TokenBlacklist {
    
    private final StringRedisTemplate redisTemplate;
    private static final String PREFIX = "blacklist:";
    
    public void invalidateToken(String token, long expireSeconds) {
        String key = PREFIX + token;
        redisTemplate.opsForValue().set(key, "true", expireSeconds, TimeUnit.SECONDS);
    }
    
    public boolean isBlacklisted(String token) {
        String key = PREFIX + token;
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
}

5.2 拦截器验证

java 复制代码
public class JwtInterceptor implements HandlerInterceptor {
    
    private final TokenBlacklist blacklist;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = extractToken(request);
        
        if (blacklist.isBlacklisted(token)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        
        // 继续验证token
        return true;
    }
}

六、JWT vs Session对比

特性 JWT Session
状态 无状态 有状态
存储 客户端 服务端
扩展性 好 差
安全性 需额外处理 相对安全
性能 网络传输大 服务器内存

七、安全检查清单

7.1 必做检查

检查项 说明
使用HTTPS 防止Token被窃取
设置过期时间 限制Token有效期
避免敏感信息 Payload是Base64编码,不是加密
使用强密钥 256位以上密钥
验证签名 防止Token被篡改

7.2 推荐检查

检查项 说明
实现刷新机制 定期轮换Token
实现黑名单 支持主动注销
限制Token大小 避免过大Payload
监控异常行为 检测暴力破解

八、总结

JWT是一种强大的身份认证机制,但需要正确使用才能保证安全:

  1. 使用HTTPS:始终通过HTTPS传输Token
  2. 合理设置过期时间:访问Token短,刷新Token长
  3. 使用非对称加密:避免密钥泄露风险
  4. 实现黑名单机制:支持主动注销
  5. 存储在安全位置:根据场景选择存储方式

通过以上措施,可以构建安全可靠的JWT认证系统。

相关推荐
Cosolar3 小时前
极简智库:快速搭建你的开源项目教程阅读平台,让 docs/ 目录被更多人看见
java·架构·github
nice_lcj5204 小时前
【从零理解 Spring AI · 16】Agent 与工作流:什么该写死,什么该让模型决定
java·人工智能·后端·spring ai·ai应用开发
北京云帆互联科技4 小时前
云帆培训考试系统更新说明:v9.3.0
java·学习
学长毕设5 小时前
基于SpringBoot的景点管理系统-附源码
java·spring boot·后端
sxlishaobin6 小时前
Maven Setting详解
java·学习·maven
知守观6 小时前
一个方法两个触发口:反编译 XXL-Job 后,我看清了 inner/job 子包的裸奔现状
java·spring boot·微服务
用户3126874877206 小时前
CompletableFuture 链式编排:异步编程的优雅解法
java
瑞雪兆丰年兮6 小时前
JavaWeb-day14-SpringBootWeb原理篇
java·开发语言·spring boot·spring·bean·mybatis·javaweb
神仙别闹7 小时前
基于 Java GUI 贪心策略直接搜索算法和极大极小博弈树算法的智能人机博弈五子棋游戏
java·毕业设计·gui·贪心策略·极大极小博弈树
Flynn Liang8 小时前
RAG 评估体系:黄金集、检索指标与 LLM-as-Judge 落地
java·langchain·rag·评估·langchain4j·检索指标