package com.trafficaudit.security.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.TextCodec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JwtUtils { private static final Logger log = LoggerFactory.getLogger(JwtUtils.class); @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; /** * 签名密钥字节:jjwt 0.9.x 默认把 secret 当 Base64 解码,若配置成中文等非法 Base64 会在登录时抛 * ArrayIndexOutOfBoundsException,表现为「登录接口 500」。这里做兜底:能按 Base64 解码就用解码结果 * (与历史 token 兼容),否则按 UTF-8 字节处理,并把提示打到日志里。 */ private byte[] secretKeyBytes() { try { return TextCodec.BASE64.decode(secret); } catch (Exception e) { log.warn("jwt.secret 不是合法的 Base64 字符串,已按 UTF-8 字节作为签名密钥;建议改成 32 位随机十六进制串。"); return secret.getBytes(StandardCharsets.UTF_8); } } public String generateToken(String username) { Map claims = new HashMap<>(); claims.put("username", username); return Jwts.builder() .setClaims(claims) .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS256, secretKeyBytes()) .compact(); } public String getUsernameFromToken(String token) { Claims claims = Jwts.parser().setSigningKey(secretKeyBytes()) .parseClaimsJws(token).getBody(); return claims.getSubject(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(secretKeyBytes()).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } /** 从 Authorization: Bearer xxx 请求头解析用户名;无效返回 null */ public String resolveUsername(String authorizationHeader) { if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) { return null; } try { return getUsernameFromToken(authorizationHeader.substring(7)); } catch (Exception e) { return null; } } }