| | |
| | | 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<String, Object> claims = new HashMap<>(); |
| | |
| | | .setSubject(username) |
| | | .setIssuedAt(new Date()) |
| | | .setExpiration(new Date(System.currentTimeMillis() + expiration)) |
| | | .signWith(SignatureAlgorithm.HS256, secret) |
| | | .signWith(SignatureAlgorithm.HS256, secretKeyBytes()) |
| | | .compact(); |
| | | } |
| | | |
| | | public String getUsernameFromToken(String token) { |
| | | Claims claims = Jwts.parser().setSigningKey(secret) |
| | | Claims claims = Jwts.parser().setSigningKey(secretKeyBytes()) |
| | | .parseClaimsJws(token).getBody(); |
| | | return claims.getSubject(); |
| | | } |
| | | |
| | | public boolean validateToken(String token) { |
| | | try { |
| | | Jwts.parser().setSigningKey(secret).parseClaimsJws(token); |
| | | 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; |
| | | } |
| | | } |
| | | } |