JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传递声明(claims)
JWT由三部分组成,用点号(.)分隔,例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ind3dy5qc29uLmNuIiwic3ViIjoiZGVtbyIsImlhdCI6MTc1MDMyMDEzOCwibmJmIjoxNzUwMzIwMTM4LCJleHAiOjE3NTA0MDY1Mzh9.KYelIqH7FqMI0KOXslGJhTYy-i7QntYKcJy2AkX2t-E

JWT基本结构

  1. Header(头部信息)
    包含令牌类型和使用的哈希算法:
    令牌类型为:JWT
    加密算法为:HS256
{
  "alg": "HS256",
  "typ": "JWT"
}
  1. Payload(负载信息 / 用户信息)
    包含声明(claims),声明是关于实体(通常是用户)和其他数据的语句。有三种类型的声明:
  • 注册声明(预定义):如iss(签发者)、exp(过期时间)、sub(主题)等
{
  "iss": "example.com",  // 签发者(Issuer)
  "sub": "user123",     // 主题(Subject,通常是用户ID)
  "aud": "api.example.com", // 受众(Audience,接收方)
  "exp": 1735689600,    // 过期时间(Expiration Time,Unix 时间戳)
  "nbf": 1735686000,    // 生效时间(Not Before)
  "iat": 1735686000     // 签发时间(Issued At)
}
  • 公共声明:可以自定义,但应在IANA JSON Web Token Registry中定义或使用抗冲突命名空间
  • 私有声明:自定义声明,用于在同意使用它们的各方之间共享信息
{
  "id": "1",
  "name": "admin",
  "admin": true
}

Payload 默认仅 Base64 编码,不加密 → 不要存储敏感信息(如密码)。
Token 一旦签发,声明不可更改(除非重新生成 Token)。
声明越多,Token 越长 → 可能影响 HTTP 头部大小限制。
3. Signature(签名信息)
用于验证消息在传递过程中没有被篡改。签名是通过将编码后的header和payload与一个密钥使用header中指定的算法生成的

整合JWT

版本为:java17

JWT依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.12.3</version>
</dependency>

基本使用

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


public class TokenUtil {
    
    //过期时间(单位:秒)
    public static final int ACCESS_EXPIRE = 60;
    //加密算法
    private final static SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256;
    /**
     * 私钥 / 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取,切记这个秘钥7不能外露,只在服务端使用,在任何场景都不应该流露出去。
     * 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
     * 应该大于等于 256位(长度32及以上的字符串),并且是随机的字符串
     */
    private final static String SECRET = "yourSecretKey1234567890123456789012345678901234567890123451xiri";
    //秘钥实例
    public static final SecretKey KEY = Keys.hmacShaKeyFor(SECRET.getBytes());
    //jwt签发者
    private final static String JWT_ISS = "Xiri";
    /** jwt主题,用于这个Token是关于谁的的,使用不可变的唯一标识.例子
     * 用户 ID(如 "sub": "user123")
     * 设备 ID(如 "sub": "device-abc")
     * 服务名称(如 "sub": "payment-service")
     */
    private final static String SUBJECT = "user";

    /*
    这些是一组预定义的声明,它们 不是强制性的,而是推荐的 ,以 提供一组有用的、可互操作的声明 。
    iss: jwt签发者
    sub: jwt所面向的用户
    aud: 接收jwt的一方
    exp: jwt的过期时间,这个过期时间必须要大于签发时间
    nbf: 定义在什么时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
     */
    public static String genAccessToken(String username) {
        // 令牌id
        String uuid = UUID.randomUUID().toString();
        Date exprireDate = Date.from(Instant.now().plusSeconds(ACCESS_EXPIRE));
        /**
         * 假设用户id和用户名
         */
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", uuid);
        claims.put("username", username);

        return Jwts.builder()
                // 设置头部信息header
                .header()
                .add("typ", "JWT")
                .add("alg", "HS256")
                .and()
                // 设置自定义负载信息payload
//                .claim("username", username)
                .claims(claims)
                // 令牌ID(jti)
                .id(uuid)
                // 过期日期
                .expiration(exprireDate)
                // 签发时间
                .issuedAt(new Date())
                // 主题
                .subject(SUBJECT)
                // 签发者
                .issuer(JWT_ISS)
                // 签名
                .signWith(KEY, ALGORITHM)
                .compact();
    }
    /**
     * 解析token
     */
    public static Jws<Claims> parseClaim(String token)  throws Exception {
        return Jwts.parser()
                .verifyWith(KEY)
                .build()
                .parseSignedClaims(token);
    }
    /**
     * 获取头
     */
    public static JwsHeader parseHeader(String token) throws Exception {
        return parseClaim(token).getHeader();
    }
    /**
     * 获取荷载
     */
    public static Claims parsePayload(String token) throws Exception {
        return parseClaim(token).getPayload();
    }

    /**
     * 验证token是否有效
     */
    public static boolean checkToken(String token){
        try {
            parseHeader(token);
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

    /**
     * 获取用户id
     */
    public static String getUserId(String token){
        try {
            Claims payload = parseClaim(token).getPayload();
            return payload.get("userId",String.class);
        }
        catch (Exception e)
        {
            return null;
        }
    }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐