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认证系统。

相关推荐
她的男孩2 分钟前
JAR 全对仍 checksum mismatch:Forge Admin 把 Flyway 从 filesystem 改成 classpath
java·数据库·spring boot
鬼手点金3 分钟前
如何选择模型
java·前端·人工智能·git·深度学习
古法老登瞎折腾10 分钟前
大数据智能运维实战(三):实现第一个运维 MCP 工具——查询节点与组件状态
java·大数据·运维·人工智能·spring ai·mcp
小蒜学长11 分钟前
基于数据挖掘的京东商城商品推荐系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·推荐系统·个性化推荐
IT研究室25 分钟前
最新计算机毕业设计选题推荐-基于spring boot的医院体检管理系统-网站-文档指导-Java-springboot
java·spring boot·课程设计
有同事要进步30 分钟前
Leecode第68题:x的平方根
java·数据结构·算法
摇滚侠32 分钟前
使用 VSCode 开发 Java 经验分享
java·经验分享·vscode
吹什么轩37 分钟前
linux系统复习:进程优先级与O(1)调度
java·linux·算法
古法老登瞎折腾43 分钟前
大数据智能运维实战(六):任务一直在等——排查调度队列与执行资源
java·大数据·运维·spring ai·mcp
Pniubi1 小时前
HashMap扩容全流程
java·开发语言·数据结构