JWT(JSON Web Token)是一种用于在不同应用之间安全传输信息的开放标准(RFC 7519)。它是一种基于 JSON 的轻量级令牌,由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。JWT 被广泛用于实现身份验证和授权,特别适用于前后端分离的应用程序

令牌类似下面这一大长串:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJxdWFueGlhb2hhIiwiaXNzIjoicXVhbnhpYW9oYSIsImlhdCI6MTY5Mjk1OTY2MSwiZXhwIjoxNjkyOTYzMjYxfQ.wbqbn23C9vAe5sQZRCBzrIM4SiN1eNl55NIONmHoiPHPHSSu0QJGgPGUin80hA4XgMHEqN1Wm5KJlmKKucUyGQ

可以看到,由 header.payload.signature 三部分组成,你可以在此网站: https://jwt.io/

JWT 提供了一种在客户端和服务器之间传输安全信息的简单方法,具有以下优点:

  • 无状态性(Stateless):JWT 本身包含了所有必要的信息,无需在服务器端存储会话信息,每个请求都可以独立验证。
  • 灵活性:JWT 可以存储任意格式的数据,使其成为传递用户信息、权限、角色等的理想选择。
  • 安全性:JWT 使用签名进行验证,确保信息在传输过程中不被篡改。
  • 跨平台和跨语言:由于 JWT 使用 JSON 格式,它在不同的编程语言和平台之间都可以轻松传递。

导入依赖

<dependency>  
    <groupId>io.jsonwebtoken</groupId>  
    <artifactId>jjwt-api</artifactId>  
    <version>0.11.5</version>  
</dependency>  
<dependency>  
    <groupId>io.jsonwebtoken</groupId>  
    <artifactId>jjwt-impl</artifactId>  
    <version>0.11.5</version>  
</dependency>  
<dependency>  
    <groupId>io.jsonwebtoken</groupId>  
    <artifactId>jjwt-jackson</artifactId>  
    <version>0.11.5</version>  
</dependency>

Jwt工具类

刚开始可以使用 generateBase64Key 来生成基于Hs512 算法的一个随机私钥,再进行一次Base64加密。

同时又使用 spring 的 @Value 方法提供 yml 配置项来配置 issuer 签发人和 secret 私钥。

import io.jsonwebtoken.*;  
import io.jsonwebtoken.security.Keys;  
import io.jsonwebtoken.security.SignatureException;  
import org.springframework.beans.factory.InitializingBean;  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.security.authentication.BadCredentialsException;  
import org.springframework.security.authentication.CredentialsExpiredException;  
import org.springframework.stereotype.Component;  
  
import java.security.Key;  
import java.time.LocalDateTime;  
import java.time.ZoneId;  
import java.util.Base64;  
import java.util.Date;  
  
@Component  
public class JwtTokenHelper implements InitializingBean {  
  
    /**  
     * 签发人  
     */  
    @Value("${jwt.issuer}")  
    private String issuer;  
    /**  
     * 秘钥  
     */  
    private Key key;  
  
    /**  
     * JWT 解析  
     */  
    private JwtParser jwtParser;  
  
    /**  
     * 解码配置文件中配置的 Base 64 编码 key 为秘钥  
     * @param base64Key  
     */  
    @Value("${jwt.secret}")  
    public void setBase64Key(String base64Key) {  
        key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(base64Key));  
    }  
  
    /**  
     * 初始化 JwtParser  
     * @throws Exception  
     */  
    @Override  
    public void afterPropertiesSet() throws Exception {  
        // 考虑到不同服务器之间可能存在时钟偏移,setAllowedClockSkewSeconds 用于设置能够容忍的最大的时钟误差  
        jwtParser = Jwts.parserBuilder().requireIssuer(issuer)  
                .setSigningKey(key).setAllowedClockSkewSeconds(10)  
                .build();  
    }  
    /**  
     * 生成 Token  
     * @param username  
     * @return  
     */  
    public String generateToken(String username) {  
        LocalDateTime now = LocalDateTime.now();  
        // Token 一个小时后失效  
        LocalDateTime expireTime = now.plusHours(1);  
  
        return Jwts.builder().setSubject(username)  
                .setIssuer(issuer)  
                .setIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))  
                .setExpiration(Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant()))  
                .signWith(key)  
                .compact();  
    }  
    /**  
     * 解析 Token  
     * @param token  
     * @return  
     */  
    public Jws<Claims> parseToken(String token) {  
        try {  
            return jwtParser.parseClaimsJws(token);  
        } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {  
            throw new BadCredentialsException("Token 不可用", e);  
        } catch (ExpiredJwtException e) {  
            throw new CredentialsExpiredException("Token 失效", e);  
        }    }  
    /**  
     * 生成一个 Base64 的安全秘钥  
     * @return  
     */  
    private static String generateBase64Key() {  
        // 生成安全秘钥  
        Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);  
  
        // 将密钥进行 Base64 编码  
        String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());  
  
        return base64Key;  
    }  
    public static void main(String[] args) {  
        String key = generateBase64Key();  
        System.out.println("key: " + key);  
    }}