文章目录

  • JWT 介绍、token结构、示范代码与性能测试
  • 介绍、token结构
  • 示范
  • 引入依赖
  • 签发token
  • 解析token
  • 性能测试
  • 签发性能测试
  • 解析token性能测试


JWT 介绍、token结构、示范代码与性能测试

介绍、token结构

JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io

JWT 介绍、token结构、示范代码与性能测试_性能测试


数据格式

JWT包含三部分数据:

  • Header:头部,通常头部有两部分内容:

声明类型,这里是JWT
签名算法,自定义
我们会对头部进行base64加密(可解密),得到第一部分数据

  • Payload:载荷,就是有效数据,一般包含下面信息:

用户身份信息(注意,这里因为采用base64加密,可直接解密,因此不要存放敏感信息)
tokenID:当前这个JWT的唯一标示
注册声明:如token的签发时间,过期时间,签发人等
这部分也会采用base64加密,得到第二部分数据

  • Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性,建议设置30天有效期等

    鉴权流程

示范

引入依赖

<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.3</version>
            <scope>compile</scope>
        </dependency>

签发token

private static final Algorithm algorithm = Algorithm.HMAC512("123sjdsfaf123eszc");
    public static final String issuer = "test";

    /**
     * 签发一个jwt token
     */
    @Test
    public void testSignToken() {
        JWTCreator.Builder builder = JWT.create();
        // 设置token签发者
        builder.withIssuer(issuer);
        // 设置token 过期时间
        builder.withExpiresAt(DateUtil.offsetDay(DateUtil.date(), 30));
        // 声明一些自定义的属性(注意不要存太长的数据、敏感数据,因为这个东西设置之后,这个token任何人拿到无需密钥,直接通过base64解码中间那一段就能拿到这些信息)
        builder.withClaim("userId", "123");
        builder.withClaim("userName", "test");
        builder.withClaim("userType", "1");
        builder.withClaim("platform", "1");
        builder.withClaim("phone", "11122223333");
        // 签发token
        String sign = builder.sign(algorithm);
        log.info("jwt:{}", sign);
    }
    }

签发出的token

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJwaG9uZSI6IjExMTIyMjIzMzMzIiwiaXNzIjoidGVzdCIsInVzZXJUeXBlIjoiMSIsImV4cCI6MTcxODQzODE4NSwidXNlck5hbWUiOiJ0ZXN0IiwidXNlcklkIjoiMTIzIiwicGxhdGZvcm0iOiIxIn0.3JDY8JSkUj1C8rqOXwJugqoL3ZBDlCRtkDZZ-eXzbWSA4SFMZl-i-G8wP5lmPlWU9xqkIG64q1Aw54UMtQzwLg

解析token

通过JWTVerifier 来解析,解析后得到DecodedJWT ,可获取这个token的所有信息。如果token被修改过、过期都会验证失败并抛出对应异常

JWT 介绍、token结构、示范代码与性能测试_java_02

private static final Algorithm algorithm = Algorithm.HMAC512("123sjdsfaf123eszc");
    public static final String issuer = "test";
  /**
     * 解析jwt和测试修改一下参数
     */
    @Test
    public void testDecodeJwtToken() {
        // 创建解析器
        JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer(issuer).build();
        // 刚才生产的token
        String sign = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJwaG9uZSI6IjExMTIyMjIzMzMzIiwiaXNzIjoidGVzdCIsInVzZXJUeXBlIjoiMSIsImV4cCI6MTcxODQzODE4NSwidXNlck5hbWUiOiJ0ZXN0IiwidXNlcklkIjoiMTIzIiwicGxhdGZvcm0iOiIxIn0.3JDY8JSkUj1C8rqOXwJugqoL3ZBDlCRtkDZZ-eXzbWSA4SFMZl-i-G8wP5lmPlWU9xqkIG64q1Aw54UMtQzwLg";
        DecodedJWT decodedJWT = jwtVerifier.verify(sign);
        String payload = decodedJWT.getPayload();

        log.info("payload:{}", payload);
        String decodeStr = Base64Decoder.decodeStr(payload);
        log.info("decodeStr:{}", decodeStr);
        String originHead = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9";
        // 直接解码出来
        log.info("originHead:{}", Base64Decoder.decodeStr(originHead));
        // 直接截取token第二段
        String originPayload = "eyJwaG9uZSI6IjExMTIyMjIzMzMzIiwiaXNzIjoidGVzdCIsInVzZXJUeXBlIjoiMSIsImV4cCI6MTcxODQzODE4NSwidXNlck5hbWUiOiJ0ZXN0IiwidXNlcklkIjoiMTIzIiwicGxhdGZvcm0iOiIxIn0";
        // 直接解码出来
        log.info("originPayload:{}", Base64Decoder.decodeStr(originPayload));
        String originSign = "3JDY8JSkUj1C8rqOXwJugqoL3ZBDlCRtkDZZ-eXzbWSA4SFMZl-i-G8wP5lmPlWU9xqkIG64q1Aw54UMtQzwLg";
        Date expiresAt = decodedJWT.getExpiresAt();
        log.info("expiresAt:{}", DateUtil.formatDate(expiresAt));

        // 改动一下token,比如改动一下userId
        JSONObject jsonObject = JSONObject.parseObject(Base64Decoder.decodeStr(originPayload));
        jsonObject.put("userId", "31221");
        String modifiedToken = originHead + "." + Base64Encoder.encode(jsonObject.toJSONString()) + "." + originSign;
        // 修改了之后的这个token就验证失败了 SignatureVerificationException
        DecodedJWT verify = jwtVerifier.verify(modifiedToken);
        log.info("verify:{}", Base64Decoder.decodeStr(verify.getPayload()));
    }

验证时token过期的异常

JWT 介绍、token结构、示范代码与性能测试_数据_03

com.auth0.jwt.exceptions.TokenExpiredException: The Token has expired on Thu May 16 16:07:39 CST 2024.

	at com.auth0.jwt.JWTVerifier.assertDateIsFuture(JWTVerifier.java:472)
	at com.auth0.jwt.JWTVerifier.assertValidDateClaim(JWTVerifier.java:463)
	at com.auth0.jwt.JWTVerifier.verifyClaims(JWTVerifier.java:404)
	at com.auth0.jwt.JWTVerifier.verify(JWTVerifier.java:387)
	at com.auth0.jwt.JWTVerifier.verify(JWTVerifier.java:370)

token改动

com.auth0.jwt.exceptions.SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA512

	at com.auth0.jwt.algorithms.HMACAlgorithm.verify(HMACAlgorithm.java:50)
	at com.auth0.jwt.JWTVerifier.verify(JWTVerifier.java:386)
	at com.auth0.jwt.JWTVerifier.verify(JWTVerifier.java:370)
	at cn.sffix.customeruser.jwt.JwtPerformanceTest.testDecodeJwtToken(JwtPerformanceTest.java:83)

性能测试

运行设备信息
处理器 Intel® Core™ i7-9700 CPU @ 3.00GHz 3.00 GHz
机带 RAM 32.0 GB (31.9 GB 可用)
系统类型 64 位操作系统, 基于 x64 的处理器

签发性能测试

/**
     * 签发token 性能测试
     */
    @Test
    public void testSignTokenPerformance() {
        int count = 10000;
        int j = count;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        while (j > 0) {
            JWTCreator.Builder builder = JWT.create();
            builder.withIssuer(issuer);
            builder.withIssuedAt(DateUtil.date());
            builder.withExpiresAt(DateUtil.offsetDay(DateUtil.date(), 30));
            builder.withClaim("userId", "123");
            builder.withClaim("userName", "test");
            builder.withClaim("userType", "1");
            builder.withClaim("platform", "1");
            builder.withClaim("phone", "11122223333");
            String sign = builder.sign(algorithm);
            j--;
        }
        stopWatch.stop();
        BigDecimal avgCostTime = BigDecimal.valueOf(stopWatch.getTotalTimeMillis()).divide(BigDecimal.valueOf(count), 3, RoundingMode.HALF_UP);
        log.info("执行 {} 次签发token耗时 {} ms 平均单词执行耗时 {} ms", count, stopWatch.getLastTaskTimeMillis(), avgCostTime);
    }

执行结果

16:04:37.045 [main] INFO cn.sffix.customeruser.jwt.JwtPerformanceTest - 执行 10000 次签发token耗时 1660 ms 平均单词执行耗时 0.166 ms

解析token性能测试

/**
     * 测试解析性能
     */
    @Test
    public void testVerifyTokenPerformance() {
        String sign = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJwaG9uZSI6IjExMTIyMjIzMzMzIiwiaXNzIjoidGVzdCIsInVzZXJUeXBlIjoiMSIsImV4cCI6MTcxODQzMzkwNSwidXNlck5hbWUiOiJ0ZXN0IiwiaWF0IjoxNzE1ODQxOTA1LCJ1c2VySWQiOiIxMjMiLCJwbGF0Zm9ybSI6IjEifQ.tCygoRcQaay-cYt_ug0QxPXrF8GJ2Er8L7NPVHjxsbxD0elWRZzHmUHzgFT3SBXXB7fX6xanCqmMy0k5cw_ovA";
        JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer(issuer).build();
        int count = 10000;
        int j = count;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        while (j > 0) {
            DecodedJWT verify = jwtVerifier.verify(sign);
            j--;
        }
        stopWatch.stop();
        BigDecimal avgCostTime = BigDecimal.valueOf(stopWatch.getTotalTimeMillis()).divide(BigDecimal.valueOf(count), 3, RoundingMode.HALF_UP);
        log.info("执行 {} 次解析token耗时 {} ms 平均每次耗时 {} ms", count, stopWatch.getLastTaskTimeMillis(), avgCostTime);
    }
16:05:29.370 [main] INFO cn.sffix.customeruser.jwt.JwtPerformanceTest - 执行 10000 次解析token耗时 1122 ms 平均每次耗时 0.112 ms