01-JWT是什么

JWT(JSON Web Token)是一种开放的行业标准(RFC 7519),用于安全地双方之间传输信息,常用于各方之间传输信息,特别是在身份认证领域使用非常广泛;

官网:https://jwt.io/

02-JWT的数据结构

JWT的数据结构如下所示:

  • 是一个很长的字符串,中间用点(.)分隔成三个部分
  • 注意,JWT内部是没有换行的,这里只是为了便于展示,将它写成了几行;

JWT的三个部分依次是:

  • Header(头部)
  • Payload(负载) 这里面可以携带业务数据(比如一些参数)
  • Signature(签名)

写成一行,就是下面的样子:Header.Payload.Signature

2.1 Header

Header部分原文是一个JSON对象,描述JWT的元数据,通常如下:

{

"alg": "HS256",

"typ": "JWT"

}

  • alg属性:表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);
  • typ属性:表示这个令牌(token)的类型(type)JWT令牌统一写为JWT;

最后,将上面的JSON对象使用 Base64URL算法 转成字符串,就得到Header部分;

2.2 Payload

Payload 部分原文也是一个JSON对象,用来 存放实际需要传递的数据,JWT定义了7个官方字段供选用:

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

我们可以不使用官方的字段,我们可以使用任何字段来传递数据,只要是个JSON就行,比如:

{

"number": "1234567890",

"name": "cat",

"phone": "13700000000"

}

这个JSON 对象也要使用 Base64URL算法 转成字符串;

注意,Base64URL 算法不是加密算法,它是 编码算法,是可以解码出原文的,也就是JWT负载中的数据任何人都可以解码得到原文(不安全),所以不要把私密信息(密码,验证码等)放在这个部分;(虽然可以解码出来,但是我们把密码比如加密之后再放在负载里面,也是没有问题,是安全的)

2.3 Signature

Signature 部分是对前两部分的签名,防止数据篡改

首先,需要指定一个密钥(secret),这个密钥只有服务器才知道,不能泄露给用户,然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式(两个参数)产生签名:

HMACSHA256(

        base64UrlEncode(header) + "." +

        base64UrlEncode(payload),

        secret)

        算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户;

base64UrlEncode(header) . base64UrlEncode(payload) . HMACSHA256(

        base64UrlEncode(header) + "." +

        base64UrlEncode(payload), secret)

样式示例:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJudW1iZXIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImNhdCIsInBob25lIjoiMTM3MDAwMDAwMDAifQ.cGLcaxSyl9dgiL_hK5wv1Pbgj8PGHFVXEWReDvW0lN4

03-SpringBoot项目中JWT使用示例

3.1 添加JWT依赖

添加 jwt 依赖的两种方式

<!-- 使用java-jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>

<!-- 使用hutool工具包 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-jwt</artifactId>
    <version>5.8.41</version>
</dependency>

3.2 使用java-jwt封装JWT工具类

封装JWT工具类使用

/**
 * JWT工具类:主要就解决三个问题
 */
public class JavaJWTUtil {

    /**
     * 密钥
     */
    public static final String secret = "day281hd9nso;scna/cnOHDUchia7U91=";

    /**
     * 生成JWT字符串
     */
    public static String createToken(String userJson) {
        //组装头数据
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        return JWT.create()
                // 头部
                .withHeader(header)
                // 负载:可以放多个数据
                .withClaim("user", userJson)
                .withClaim("phone", "13133682689")
                .withClaim("birthday", new Date(1990, Calendar.MARCH, 3))
                // 签名
                .sign(Algorithm.HMAC256(secret));
    }

    /**
     * 验证JWT有没有被篡改
     *
     * @param token 要验证的jwt的字符串
     */
    public static Boolean verifyToken(String token) {
        try {
            // 使用密钥创建一个jwt解析对象
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(secret)).build();
            // 使用验证器对象验证JWT,如果验证没有抛出异常,则验证通过;反之就是没通过
            jwtVerifier.verify(token);
            // 如果没有抛出异常,返回 true 表示验证通过
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解析出JWT里面的负载数据
     *
     * @param token 要解析的jwt的字符串
     * @return
     */
    public static String parseToken(String token) {
        try {
            // 使用密钥创建一个jwt验证器对象
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(secret)).build();
            // 验证JWT,得到一个解码后的JWT对象
            DecodedJWT decodedJWT = jwtVerifier.verify(token);

            // 通过解码后的验证器对象获取负载数据
            Claim user = decodedJWT.getClaim("user");
            System.out.println(user.asString());
            Claim phone = decodedJWT.getClaim("phone");
            System.out.println(phone.asString());
            Claim birthday = decodedJWT.getClaim("birthday");
            System.out.println(birthday.asDate());

            return user.asString();
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

编写测试代码:

public class testJavaJwt {
    public static void main(String[] args) {

        TUser tUser = TUser.builder().id(10089).loginAct("John").email("3072107986@qq.com").build();
        String jsonStr = JSONUtil.toJsonStr(tUser);

        // 生成JWT字符串
        String token = createToken(jsonStr);
        System.out.println(token);

        // 验证JWT
        Boolean verify = verifyToken(token + 123);
        System.out.println("验证结果:" + verify);

        // 解析JWT
        parseToken(token);
    }
}

运行效果:验证时传入的token和之前生成时使用的token不一致,解析失败,抛出异常

3.3 使用hutool-jwt操作jwt

使用 hutool 包就不用自己写工具类了,它基本都封装好了

3.3.1 生成JWT

JWTUtil.createToken() 方法生成 JWT

public class HutoolJWTTest {

    // 密钥不能让别人知道,这个密钥放在服务器上
    public static final String secret = "0S/12d4845Whd2h3OPYs";

    public static void main(String[] args) {

        // 生成JWT
        Map<String, Object> header = new HashMap<>();   // 头部
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        Map<String, Object> payLoad = new HashMap<>();  // 负载
        payLoad.put("id", 12352);
        payLoad.put("phone", "13720202020");
        payLoad.put("birthDay", new Date());

        // 四个重载方法
        String jwt = JWTUtil.createToken(payLoad, secret.getBytes(StandardCharsets.UTF_8));
        System.out.println(jwt);

        String jwt2 = JWTUtil.createToken(payLoad, JWTSignerUtil.hs256(secret.getBytes(StandardCharsets.UTF_8)));
        System.out.println(jwt2);

        String jwt3 = JWTUtil.createToken(header, payLoad, secret.getBytes(StandardCharsets.UTF_8));
        System.out.println(jwt3);

        String jwt4 = JWTUtil.createToken(header, payLoad, JWTSignerUtil.hs256(secret.getBytes(StandardCharsets.UTF_8)));
        System.out.println(jwt4);
    }
}

可以看到,运行效果都是一样的

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJiaXJ0aERheSI6MTc2NTI3MDQ1NiwicGhvbmUiOiIxMzcyMDIwMjAyMCIsImlkIjoxMjM1Mn0.4VLZXBdQ3CJOKuppMCZ1XiStatrcQv1ueMKFTxgzCAI
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJiaXJ0aERheSI6MTc2NTI3MDQ1NiwicGhvbmUiOiIxMzcyMDIwMjAyMCIsImlkIjoxMjM1Mn0.4VLZXBdQ3CJOKuppMCZ1XiStatrcQv1ueMKFTxgzCAI
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJiaXJ0aERheSI6MTc2NTI3MDQ1NiwicGhvbmUiOiIxMzcyMDIwMjAyMCIsImlkIjoxMjM1Mn0.4VLZXBdQ3CJOKuppMCZ1XiStatrcQv1ueMKFTxgzCAI
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJiaXJ0aERheSI6MTc2NTI3MDQ1NiwicGhvbmUiOiIxMzcyMDIwMjAyMCIsImlkIjoxMjM1Mn0.4VLZXBdQ3CJOKuppMCZ1XiStatrcQv1ueMKFTxgzCAI

3.3.2 验证JWT

JWTUtil.verify() 方法验证 JWT

public class HutoolJWTTest {

    // 密钥不能让别人知道,这个密钥放在服务器上
    public static final String secret = "0S/12d4845Whd2h3OPYs";

    public static void main(String[] args) {

        // 生成JWT
        Map<String, Object> header = new HashMap<>();   // 头部
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        Map<String, Object> payLoad = new HashMap<>();  // 负载
        payLoad.put("id", 12352);
        payLoad.put("phone", "13720202020");
        payLoad.put("birthDay", new Date());

        String jwt4 = JWTUtil.createToken(header, payLoad, JWTSignerUtil.hs256(secret.getBytes(StandardCharsets.UTF_8)));

        // 2、验证jwt	两个负载方法
        boolean verify = JWTUtil.verify(jwt4, secret.getBytes(StandardCharsets.UTF_8));
        boolean verify2 = JWTUtil.verify(jwt4, JWTSignerUtil.hs256(secret.getBytes(StandardCharsets.UTF_8)));
        System.out.println(verify);
        System.out.println(verify2);
    }
}

运行效果:

true
true

3.3.3 解析JWT

* 使用 JWTPayload 中的 getClaim() 方法返回 Object 对象,除了String,其它类型进行强制类型转换会报错

public class HutoolJWTTest {

    // 密钥不能让别人知道,这个密钥放在服务器上
    public static final String secret = "0S/12d4845Whd2h3OPYs";

    public static void main(String[] args) {

        // 生成JWT
        Map<String, Object> header = new HashMap<>();   // 头部
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        Map<String, Object> payLoad = new HashMap<>();  // 负载
        payLoad.put("id", 12352);
        payLoad.put("phone", "13720202020");
        payLoad.put("birthDay", new Date());

        String jwt4 = JWTUtil.createToken(header, payLoad, JWTSignerUtil.hs256(secret.getBytes(StandardCharsets.UTF_8)))

        //3、解析jwt负载中的数据
        JWT parseJWT = JWTUtil.parseToken(jwt4);
        // 拿数据一般就是拿 payload 中的
        JWTPayload payload = parseJWT.getPayload();
        // getClaim() 方法返回 Object 对象
        Object id = payload.getClaim("id");
        String phone = (String) payload.getClaim("phone");
        Object birthDay = payload.getClaim("birthDay");
        System.out.println(id + " -- " + phone + " -- " + birthDay);
    }
}

// 结果:12352 -- 13720202020 -- 1765271748

* 使用 getPayloads() 方法获取 JSONObject 类对象,调用它的 get() 方法并指定属性类型;payload中就是按JSON格式存储的

public class HutoolJWTTest {

    public static void main(String[] args) {
        // 生成 和 验证 代码同上 ……
        
        //3、解析jwt负载中的数据
        JWT parseJWT = JWTUtil.parseToken(jwt4);
        // 拿数据一般就是拿 payload 中的
        JSONObject payloads = parseJWT.getPayloads();
        Integer id2 = payloads.get("id", Integer.class);
        String phone2 = payloads.get("phone", String.class);
        Date birthDay2 = payloads.get("birthDay", Date.class);
        System.out.println(id2 + " -- " + phone2 + " -- " + birthDay2);
    }
}

// 结果:12352 -- 13720202020 -- 2025-12-09 17:15:48

* 使用 getPayload(String name) 方法获取到 Object 对象,也不能进行向下转型

public class HutoolJWTTest {

    public static void main(String[] args) {
        // 生成 和 验证 代码同上 ……

        //3、解析jwt负载中的数据
        JWT parseJWT = JWTUtil.parseToken(jwt4);
        // 拿数据一般就是拿 payload 中的
        Object id3 = parseJWT.getPayload("id");
        Object phone3 = parseJWT.getPayload("phone");
        Object birthDay3 = parseJWT.getPayload("birthDay");
        System.out.println(id3 + " -- " + phone3 + " -- " + birthDay3);
    }
}

// 结果:12352 -- 13720202020 -- 1765271748

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐