场景

JWT通常用于用户登陆状态的校验

  1. token可以作为redis数据库中的key,而value则是保存的数据,如用户信息等
  2. token本身的Payload部分也可以存储一些简单的信息,如用户id等

JWT

JWT是什么?

JWT 是一种基于JSON的开放标准(RFC 7519),用于在网络应用环境中传输声明信息。声明的信息可以被数字签名以保证数据的完整性和安全性。JWT通常用于身份认证系统,服务器通过生成一个JWT来标识用户,并且客户端会在后续的每次请求中携带该JWT进行身份验证。

JWT的组成部分

JWT包含三个部分,每部分之间通过.分隔:

  1. Header(头部):描述JWT的元信息,如签名算法。
  2. Payload(载荷):存储具体的用户信息,如用户ID、权限等。
  3. Signature(签名):确保JWT未被篡改。

JWT的格式如下: Header.Payload.Signature

接下来,我们将详细讲解这三部分的结构和作用。

Header(头部)

JWT的头部通常是一个简单的JSON对象,包含两个字段: alg:签名算法,比如HS256表示HMAC-SHA256。 typ:声明这个令牌的类型,通常为JWT。 一个示例头部:

{
  "alg": "HS256",
  "typ": "JWT"
}

然后,这个JSON对象会被Base64编码,得到JWT的第一部分。

Payload(载荷)

载荷部分包含实际传输的声明信息。JWT标准规定了7个默认的字段(称为”registered claims”),包括: iss(Issuer):签发者 exp(Expiration Time):过期时间 sub(Subject):主题 aud(Audience):接收方 iat(Issued At):签发时间 当然,你也可以自定义一些字段,例如用户的ID或角色。一个示例的payload:

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

同样,Payload部分会被Base64编码,得到JWT的第二部分。

Signature(签名)

签名部分用于验证JWT的完整性,防止Token被篡改。签名是通过对Header和Payload进行Base64编码,并使用指定的算法和密钥进行签名生成的。

签名的生成过程如下:

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

这个签名确保了JWT的安全性,只有持有密钥的一方才能验证和生成JWT。

JWT的生成与解析

接下来,我们将通过代码来演示如何在实际项目中生成和解析JWT。

生成JWT

假设我们使用Java语言,并通过java-jwt库来生成和解析JWT。首先,添加依赖:

<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.18.2</version>
</dependency>

然后我们来演示如何生成一个JWT:

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;

public class JwtDemo {
    public static void main(String[] args) {
        Algorithm algorithm = Algorithm.HMAC256("secret");  // 使用HMAC256算法
        String token = JWT.create()
                .withIssuer("auth0")          // 签发者
                .withSubject("1234567890")    // 用户ID
                .withClaim("name", "John Doe")  // 自定义字段
                .withClaim("admin", true)     // 自定义字段
                .withIssuedAt(new Date())     // 签发时间
                .withExpiresAt(new Date(System.currentTimeMillis() + 3600 * 1000))  // 过期时间
                .sign(algorithm);             // 使用指定算法进行签名
        
        System.out.println("Generated JWT: " + token);
    }
}

解析JWT

当客户端携带JWT请求时,服务器需要对JWT进行解析和验证。下面是解析JWT的示例:

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.JWTVerifier;

public class JwtParser {
    public static void main(String[] args) {
        String token = "your.jwt.token.here";  // 待解析的JWT
        
        Algorithm algorithm = Algorithm.HMAC256("secret");  // 使用同一密钥
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build(); // 构建JWT验证器
        DecodedJWT jwt = verifier.verify(token);  // 验证并解析JWT

        System.out.println("Subject: " + jwt.getSubject());
        System.out.println("Name: " + jwt.getClaim("name").asString());
        System.out.println("Admin: " + jwt.getClaim("admin").asBoolean());
    }
}

封装JWT为工具类

public class JwtUtils {

    private static Algorithm hmac256 = Algorithm.HMAC256("YLWTSMTJFYHDCMGSCWHSSYBZSDKC");
    /**
     * 生成token
     * @param pub  负载
     * @param expiresTime 过期时间(单位 毫秒)
     * @return token
     */
    public static String sign(String pub, Long expiresTime){
        return JWT.create() //生成令牌函数
                .withIssuer(pub) //用iss字段存储用户id,当然也可以自定义一个新字段
                .withExpiresAt(new Date(System.currentTimeMillis()+expiresTime)) //添加过期时间,单位为ms
                .sign(hmac256);
    }
    /**
     * 校验token
     */
    public static boolean verify(String token){
        JWTVerifier verifier = JWT.require(hmac256).build();
        //如果正确,直接代码向下执行,如果错误,抛异常
        verifier.verify(token);
        return true;
    }
    /**
     * 从token中获取负载
     * @param token 令牌
     * @return 保存的负载
     */
    public static String getClaim(String token){
        DecodedJWT jwt = JWT.decode(token);
        Claim iss = jwt.getClaim("iss");
        return iss.asString();
    }
}