JWT令牌

131 阅读5分钟

1.什么是JWT官网

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络上安全传输信息的简洁、自包含的方式。它通常被用于身份验证和授权机制。 JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),JWT可以使用secret(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

2.什么时候应该使用JWT令牌

授权:这是使用JWT最常见的场景。一旦用户登录,后续的每个请求都将包括JWT,从而允许用户访问该令牌允许的路由、服务和资源。单点登录是目前JWT广泛使用的一个功能,因为它的开销很小,而且可以很容易地跨不同的域使用。

信息交换:JSON Web令牌是在各方之间安全传输信息的好方法。因为JWTs是可以签名的--例如,使用公钥/私钥对--您可以确保发送者就是他们所说的那个人。此外,由于签名是使用头和有效负载计算的,因此您还可以验证内容是否未被篡改。

3.JWT令牌结构是什么?

JWT令牌由三部分组成,由.号分割

Header
Payload
Signature

因此,JWT通常如下所示。

xxxxx.yyyyy.zzzzz

Header

	Header通常由两部分组成:令牌的类型(JWT)和使用的签名算法(如HMAC SHA256或RSA)。
{
  "alg": "HS256",
  "typ": "JWT"
}

然后,对该JSON进行Base64Url编码,以形成JWT的第一部分。


Payload

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims.

也称为JWT claims,放置需要传输的信息,有三类:

**保留claims:**主要包括iss发行者、exp过期时间、sub主题、aud用户等。

**公共claims:**定义新创的信息,比如用户信息和其他重要信息。

**私有claims:**用于发布者和消费者都同意以私有的方式使用的信息

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Signatrue(签名信息)

官方解释:

To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

For example if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

签名用于验证消息在发送过程中没有被更改,并且,对于使用私钥签名的令牌,它还可以验证JWT的发送者是否就是它所说的那个人


4.基于HS256签名算法的JWT实例

引入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jwt.version}</version> <!-- 根据最新版本更新 -->
</dependency>

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>${jaxb.version}</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>${activation.version}</version>
</dependency>

<properties>
      <jwt.version>0.9.1</jwt.version>
      <jaxb.version>2.3.1</jaxb.version>
        <activation.version>1.1.1</activation.version>
</properties>


/**
 * @param header
 * @param claims
 * @param base64EncodedSecretKey
 * @return java.lang.String
 * @description 基于HS256
 * @author majw
 * @date 2024/03/22 14:22
 */
public static String generateToken_HS256(Map<String, Object> header, Map<String, Object> claims, String base64EncodedSecretKey) {
    return Jwts.builder()
            .setHeader(header)
            .setClaims(claims)
            .setIssuedAt(Date.from(Instant.now()))
            .setExpiration(Date.from(Instant.now().plusSeconds(TOKEN_VALIDITY_IN_SECONDS)))
            .signWith(SignatureAlgorithm.HS256, base64EncodedSecretKey)
            .compact();
}

/**
 * @param token
 * @param base64EncodedSecretKey
 * @return io.jsonwebtoken.Claims
 * @description 解析token
 * @author majw
 * @date 2024/03/22 14:30
 */
public static Claims parseToken_HS256(String token, String base64EncodedSecretKey) {
    try {
        return Jwts.parser()
                .setSigningKey(base64EncodedSecretKey)
                .parseClaimsJws(token)
                .getBody();
    } catch (Exception e) {
        throw new IllegalArgumentException("Invalid JWT token");
    }
}



public static void main(String[] args) throws Exception {
    String key = "ijopjjojooohjpkpjbuhgyfygyhiuygyufkpkjokjokpkljiyuhnjohjjlkpljkojmijkiojhiojkojojojopjopjkhyugygbjhh";
    // 1. 定义header部分内容
    Map headerMap = new HashMap();
    headerMap.put("alg", SignatureAlgorithm.HS256.getValue());
    headerMap.put("typ", "JWT");

    // 2. 定义payload部分内容
    Map payloadMap = new HashMap();
    payloadMap.put("sub", "爱吃橙子的猫");
    payloadMap.put("id", UUID.randomUUID());
    payloadMap.put("exp", System.currentTimeMillis() + 24 * 60 * 60 * 1000);
    payloadMap.put("name", "爱吃橙子的猫桑");
    payloadMap.put("role", "爱吃橙子的猫桑");

    final String hs256Token = generateToken_HS256(headerMap, payloadMap, Base64Encoder.encode(key));
    System.out.println(hs256Token);

    final Claims claims = parseToken_HS256(hs256Token, Base64Encoder.encode(key));
    System.out.println(claims.get("sub"));
    System.out.println(claims.get("id"));
    System.out.println(claims.get("exp"));
    System.out.println(claims.get("name"));
    System.out.println(claims.get("role"));
}

man函数的调用结果

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJzdWIiOiLniLHlkIPmqZnlrZDnmoTnjKsiLCJyb2xlIjoi54ix5ZCD5qmZ5a2Q55qE54yr5qGRIiwibmFtZSI6IueIseWQg-apmeWtkOeahOeMq-ahkSIsImlkIjoiYzI3YjY1ODYtODI1MS00NWM4LThhOGQtM2JjY2E5OThjMjBjIiwiZXhwIjoxNzExMTE3NzczLCJpYXQiOjE3MTEwODg5NzN9
.yKnhW_sYnruTA80UVGeQvXMc5-kVNOsuVs_ownG_6Rg
爱吃橙子的猫
c27b6586-8251-45c8-8a8d-3bcca998c20c
1711117773
爱吃橙子的猫桑
爱吃橙子的猫桑

生成token在官网验证验证工具地址

8a16ec59687a21611193a029e71d6c2b.png

5.基于RSA签名算法的JWT实例


引入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jwt.version}</version> <!-- 根据最新版本更新 -->
</dependency>

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>${jaxb.version}</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>${activation.version}</version>
</dependency>

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>${hutool-all.version}</version>
</dependency>

<properties>
    <jwt.version>0.9.1</jwt.version>
    <jaxb.version>2.3.1</jaxb.version>
    <activation.version>1.1.1</activation.version>
     <hutool-all.version>5.8.26</hutool-all.version>
</properties>




JWT实例


/**
 * @Description:
 * @Author: 爱吃橙子的
 * @Date: 2024/3/20
 **/
public class JwtUtil {
    public static final String ISSUER = "your-app-issuer";
    public static final String HEADERS_TOKEN_KEY = "token";
    public static final long TOKEN_VALIDITY_IN_SECONDS = 60 * 60 * 8; // 8小时有效期

 
    public static final String RSA_PRIVATE_KEY = "your private key";
    public static final String RSA_PUBLIC_KEY = "your public key";

    public static String generateToken_RSA(Map<String, Object> claims) {
        try {
            return generateToken_RSA(claims, base64ToPrivateKey(RSA_PRIVATE_KEY));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param claims
     * @param privateKey
     * @return java.lang.String
     * @description 申城jwttoken
     * @author majw
     * @date 2024/03/20 15:03
     */
    public static String generateToken_RSA(Map<String, Object> claims, PrivateKey privateKey) {
        Map<String, Object> jwtClaims = new HashMap<>(claims);
        return Jwts.builder()
                .setHeader(new HashMap<String, Object>() {{
                    put("typ", "jwt");
                    put("alg", "RS256");
                }})
                .setClaims(jwtClaims)
                .setIssuedAt(Date.from(Instant.now()))
                .setExpiration(Date.from(Instant.now().plusSeconds(TOKEN_VALIDITY_IN_SECONDS)))
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }

    public static Claims parseToken_RSA(String token, PublicKey publicKey) {
        try {
            return Jwts.parser()
                    .setSigningKey(publicKey)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid JWT token");
        }
    }


    // 示例:生成RSA密钥对

    /**
     * @return java.security.KeyPair
     * @description 生成RSA密钥对
     * @author majw
     * @date 2024/03/22 14:19
     */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048); // 生成2048位密钥对
        return keyGen.generateKeyPair();
    }

    /**
     * @param publicKey
     * @return java.security.PublicKey
     * @description PublicKey转为base64编码的字符串
     * @author majw
     * @date 2024/03/22 14:18
     */
    public static String publicKeyToBase64(PublicKey publicKey) throws Exception {
        // 获取公钥的字节表示形式
        byte[] publicKeyBytes = publicKey.getEncoded();
        // 使用Base64进行编码
        String base64EncodedKey = Base64.getEncoder().encodeToString(publicKeyBytes);
        return base64EncodedKey;
    }

    /**
     * @param privateKey
     * @return java.security.PublicKey
     * @description PrivateKey转为base64编码的字符串
     * @author majw
     * @date 2024/03/22 14:18
     */
    public static String privateKeyToBase64(PrivateKey privateKey) throws Exception {
        // 获取公钥的字节表示形式
        byte[] privateKeyBytes = privateKey.getEncoded();
        // 使用Base64进行编码
        String base64EncodedKey = Base64.getEncoder().encodeToString(privateKeyBytes);
        return base64EncodedKey;
    }

    /**
     * @param base64EncodedKey
     * @return java.security.PublicKey
     * @description base64编码的字符串key转为PrivateKey
     * @author majw
     * @date 2024/03/22 14:18
     */
    public static PrivateKey base64ToPrivateKey(String base64EncodedKey) throws Exception {
        // 将Base64编码的字符串解码为字节数组
        byte[] decodedKey = Base64.getDecoder().decode(base64EncodedKey);
        // 创建一个PKCS8EncodedKeySpec对象,用于解析密钥材料
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        // 创建KeyFactory实例,这里假设是RSA算法
        KeyFactory kf = KeyFactory.getInstance("RSA");
        // 根据密钥规范生成PrivateKey对象
        PrivateKey privateKey = kf.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * @param base64EncodedKey
     * @return java.security.PublicKey
     * @description base64编码的字符串key转为PublicKey
     * @author majw
     * @date 2024/03/22 14:18
     */
    public static PublicKey base64ToPublicKey(String base64EncodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 将Base64编码的字符串解码为字节数组
        byte[] decodedKey = Base64.getDecoder().decode(base64EncodedKey);
        // 创建一个X509EncodedKeySpec对象,用于解析密钥材料
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        // 创建KeyFactory实例,这里假设是RSA算法
        KeyFactory kf = KeyFactory.getInstance("RSA");
        // 根据密钥规范生成PublicKey对象
        PublicKey publicKey = kf.generatePublic(keySpec);
        return publicKey;
    }

}