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

相关推荐
西安邮电大学1 小时前
分治算法详细讲解
java·后端·其他·算法·面试
摇滚侠1 小时前
Mybatis 入门到项目实战 搭建 MyBatis 框架 01-14
java·tomcat·mybatis
cfm_29141 小时前
JVM底层源码深度解析:读写屏障(Read/Write Barrier)
jvm
码不停蹄的玄黓2 小时前
SpringBoot 全局异常处理器实现
java·spring boot·后端
小高学习java2 小时前
事务的边界问题,如何判断数据回滚时机。
java·数据库·后端
何极光2 小时前
Maven安装与配置
java·maven
Ting.~2 小时前
在java中接入百度地图
java·开发语言·dubbo
敲个大西瓜2 小时前
加密算法小解
java
阿维的博客日记2 小时前
怎么样才算是用到了反射呢?有什么关键特征吗
java
wuminyu3 小时前
Java世界中StringTable源码剖析
java·linux·c语言·jvm·c++