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

相关推荐
qq7422349849 小时前
全面深入的C#核心知识体系与编程实践精要——从语法基础到高级特性系统学习指南
java·算法·c#
萌新小码农‍9 小时前
Python的input函数
java·前端·python
NiceCloud喜云9 小时前
AutoClaw 接入自定义 Anthropic 端点:让 Kanban 工作流跑在自己的模型路由上
java·开发语言·c++·人工智能·python·eclipse·batch
马尔斯的蓝色9 小时前
[特殊字符] Java 集成 Elasticsearch 实战指南
java
aloha_7899 小时前
信息系统项目管理师选择题考前真题错题笔记汇总
java·笔记·学习·tomcat
tongluowan0079 小时前
jvm垃圾回收器 - ZGC
java·jvm·zgc·垃圾回收器
Dicky-_-zhang9 小时前
API安全设计与防护实战
java·jvm
aloha_7899 小时前
信息系统项目管理师真题做题笔记
java·笔记·学习·软件工程·学习方法
努力发光的程序员9 小时前
互联网大厂Java面试故事:Spring Boot与微服务全栈技术实战问答
java·spring boot·spring cloud·微服务·kafka·hibernate·面试技巧