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

相关推荐
泡海椒9 小时前
JQuick-Excel 项目定位与适用场景:声明式 Java Excel 的边界
java·开发语言·excel
AC赳赳老秦9 小时前
财报附注表格精准提取:OpenClaw 从 PDF 年报附注挖掘隐藏明细,补齐财务分析维度
java·汇编·c++·python·青少年编程·deepseek·openclaw
箓维9 小时前
线程的优缺点,与进程的关联和差异
java·服务器·笔记
曹牧9 小时前
Spring MVC:@RequestMapping
java·spring·mvc
曹牧9 小时前
Spring MVC : Controller 层URL划分
java·运维·服务器·前端
卓怡学长9 小时前
w203基于springboot钢材仓库管理系统的设计与实现
java·spring boot·spring·intellij-idea
sunburn-9 小时前
Java 排序算法详细教学:从冒泡排序到快速排序
java·开发语言·数据结构·ide·算法
我是小白呀9 小时前
编排还是事件协作?用 AcmeFlow 讲清企业 Workflow 的控制权
java·开发语言
深入云栈10 小时前
Netty 4.2.x 源码深度解析 (十四):KQueue 传输 —— macOS 高性能 IO 的 Netty 实现
java·后端
信誓旦旦的程序猿10 小时前
【Python 量化取数指南 #13】Python 把行情落库:sqlite 一键存,回测随用随取
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据api