美文网首页
jose.4.j JWT

jose.4.j JWT

作者: 又语 | 来源:发表于2020-04-15 20:45 被阅读0次

    本文介绍基于 jose.4.j 的 JWT 实现方法。


    目录

    • jose.4.j 简介
    • 代码示例

    jose.4.j 简介

    jose.4.j 库是基于 Apache License 2.0 协议开源的 JWT 和 JOSE(Javascript Object Signing and Encryption) 规范套件,用 Java 代码编写实现并依赖于 JCA API 实现加密。


    代码示例

    1. 添加依赖
    <dependency>
        <groupId>org.bitbucket.b_c</groupId>
        <artifactId>jose4j</artifactId>
        <version>0.7.0</version>
    </dependency>
    
    1. 编写代码
    package tutorial.jwt;
    
    import org.jose4j.jwa.AlgorithmConstraints;
    import org.jose4j.jwk.RsaJsonWebKey;
    import org.jose4j.jwk.RsaJwkGenerator;
    import org.jose4j.jws.AlgorithmIdentifiers;
    import org.jose4j.jws.JsonWebSignature;
    import org.jose4j.jwt.JwtClaims;
    import org.jose4j.jwt.MalformedClaimException;
    import org.jose4j.jwt.consumer.ErrorCodes;
    import org.jose4j.jwt.consumer.InvalidJwtException;
    import org.jose4j.jwt.consumer.JwtConsumer;
    import org.jose4j.jwt.consumer.JwtConsumerBuilder;
    import org.jose4j.keys.HmacKey;
    import org.jose4j.lang.JoseException;
    import org.junit.Test;
    
    import java.security.Key;
    
    public class Jose4JTest {
    
        @Test
        public void testHMAC() throws JoseException {
            // 注意密钥长短(最少32个字符)
            HmacKey hmacKey = new HmacKey("12345678123456781234567812345678".getBytes());
            JsonWebSignature jsonWebSignature = jsonWebSignature(hmacKey, AlgorithmIdentifiers.HMAC_SHA256);
            String jwt = jsonWebSignature.getCompactSerialization();
            JwtConsumer jwtConsumer = jwtConsumer(hmacKey, AlgorithmIdentifiers.HMAC_SHA256);
            try {
                // 校验 JWT 并将其处理成 JwtClaims 对象
                JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
                System.out.println("JWT validation succeeded! JwtClaims: " + jwtClaims);
            } catch (InvalidJwtException e) {
                handleException(e);
            }
        }
    
        @Test
        public void testRSA() throws JoseException {
            // 生成 RSA 密钥对(打包在 JsonWebKey 中),将用于签名和验签 JWT
            RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
            // 给 JsonWebKey 赋值一个密钥 ID(可选操作)
            rsaJsonWebKey.setKeyId("key1");
            JsonWebSignature jsonWebSignature = jsonWebSignature(rsaJsonWebKey.getPrivateKey(),
                    rsaJsonWebKey.getKeyId(),
                    AlgorithmIdentifiers.RSA_USING_SHA256);
            /*
             * 签名 JWS,生成 JWT 字符串
             * 如果想加密此字符串,则将此结果作为 JsonWebEncryption 对象的负载,并将 cty(Content Type)头设置为 jwt
             */
            String jwt = jsonWebSignature.getCompactSerialization();
            /*
             * 使用 JwtConsumerBuilder 构建一个合适的 JwtConsumer 对象,用于校验和处理 JWT。
             * 如果 JWT 已被加密,只需提供一个解密密钥或解密密钥解析器给 JwtConsumerBuilder。
             */
            JwtConsumer jwtConsumer = jwtConsumer(rsaJsonWebKey.getKey(), AlgorithmIdentifiers.RSA_USING_SHA256);
            try {
                // 校验 JWT 并将其处理成 JwtClaims 对象
                JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
                System.out.println("JWT validation succeeded! JwtClaims: " + jwtClaims);
            } catch (InvalidJwtException e) {
                handleException(e);
            }
        }
    
        private JsonWebSignature jsonWebSignature(Key key, String algorithm) {
            return jsonWebSignature(key, null, algorithm);
        }
    
        /**
         * 一个 JWT 是一个携带 JwtClaims 作为负载的 JsonWebSignature 或 JsonWebEncryption 对象
         * 本例中的 JWT 是一个 JsonWebSignature 对象
         */
        private JsonWebSignature jsonWebSignature(Key key, String kid, String algorithm) {
            JsonWebSignature jsonWebSignature = new JsonWebSignature();
            // 为 JsonWebSignature 对象添加负载:JwtClaims 对象的 Json 内容
            jsonWebSignature.setPayload(jwtClaims().toJson());
            // JWT 使用 RSA 私钥签名
            jsonWebSignature.setKey(key);
            // 可选操作
            if (null != key) {
                jsonWebSignature.setKeyIdHeaderValue(kid);
            }
            // 在 JWT / JWS 上设置签名算法
            jsonWebSignature.setAlgorithmHeaderValue(algorithm);
            return jsonWebSignature;
        }
    
        /**
         * 创建 Claims,包装了 JWT 的内容
         */
        private JwtClaims jwtClaims() {
            JwtClaims claims = new JwtClaims();
            // 设置 Token 的签发者
            claims.setIssuer("Issuer");
            // 设置过期时间
            // claims.setExpirationTime();
            // 设置过期时间为 10 分钟后
            claims.setExpirationTimeMinutesInTheFuture(10);
            claims.setSubject("Subject");
            // 设置 Token 将被发送给哪些对象
            claims.setAudience("Audience X", "Audience Y", "Audience Z");
            // claims.setNotBefore();
            // 设置生效时间为 2 分钟前
            claims.setNotBeforeMinutesInThePast(2);
            // claims.setIssuedAt();
            // 设置 Token 发布/创建 时间为当前时间
            claims.setIssuedAtToNow();
            // claims.setJwtId();
            // 为 JWT 设置一个自动生成的唯一 ID
            claims.setGeneratedJwtId();
            // 额外添加的生命属性
            claims.setClaim("email", "email@example.com");
            return claims;
        }
    
        /**
         * 使用 JwtConsumerBuilder 构建一个合适的 JwtConsumer 对象,用于校验和处理 JWT。
         * 如果 JWT 已被加密,只需提供一个解密密钥或解密密钥解析器给 JwtConsumerBuilder。
         */
        private JwtConsumer jwtConsumer(Key key, String algorithm) {
            return new JwtConsumerBuilder()
                    // 在验证时间时留出一些余量以解决时钟偏差问题
                    .setAllowedClockSkewInSeconds(30)
                    // 设置解密密钥
                    // .setDecryptionKey()
                    // 设置解密密钥解析器
                    // .setDecryptionKeyResolver()
                    // .setDisableRequireSignature()
                    // 必须设置过期时间
                    .setRequireExpirationTime()
                    // 必须设置 Subject
                    .setRequireSubject()
                    // 必须设置 Token 签发者
                    .setExpectedIssuer("Issuer")
                    // 必须设置 Token 签发给谁
                    .setExpectedAudience("Audience X")
                    // 设置用于验证签名的公钥
                    .setVerificationKey(key)
                    // 设置允许的预期签名算法
                    .setJwsAlgorithmConstraints(
                            AlgorithmConstraints.ConstraintType.WHITELIST, algorithm)
                    .build();
        }
    
        /**
         * 处理校验 JWT 并将其处理成 JwtClaims 对象过程中出现的异常
         */
        private void handleException(InvalidJwtException e) {
            System.out.println("Invalid JWT:" + e);
            try {
                JwtClaims jwtClaims = e.getJwtContext().getJwtClaims();
                // 异常是否因 JWT 过期触发
                if (e.hasExpired()) {
                    System.out.println("Expired at " + jwtClaims.getExpirationTime());
                }
                // 异常是否因 Audience 无效触发
                if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) {
                    System.out.println("Invalid audience: " + jwtClaims.getAudience());
                }
                // 异常是否因缺少 Audience 触发
                if (e.hasErrorCode(ErrorCodes.AUDIENCE_MISSING)) {
                    System.out.println("Audience missing!");
                }
                // 异常是否因缺少加密触发
                if (e.hasErrorCode(ErrorCodes.ENCRYPTION_MISSING)) {
                    System.out.println("Encryption missing!");
                }
                // 异常是否因缺少过期时间触发
                if (e.hasErrorCode(ErrorCodes.EXPIRATION_MISSING)) {
                    System.out.println("Expiration missing!");
                }
                // 异常是否因过期时间太长触发
                if (e.hasErrorCode(ErrorCodes.EXPIRATION_TOO_FAR_IN_FUTURE)) {
                    System.out.println("Expiration too far in future: " + jwtClaims.getExpirationTime());
                }
                // 异常是否因缺乏完整性触发
                if (e.hasErrorCode(ErrorCodes.INTEGRITY_MISSING)) {
                    System.out.println("Integrity missing!");
                }
                // 异常是否因发布时间无效触发
                if (e.hasErrorCode(ErrorCodes.ISSUED_AT_INVALID_FUTURE)) {
                    System.out.println("Issued at invalid future: " + jwtClaims.getIssuedAt());
                }
                // 异常是否因发布时间无效触发
                if (e.hasErrorCode(ErrorCodes.ISSUED_AT_INVALID_PAST)) {
                    System.out.println("Issued at invalid past: " + jwtClaims.getIssuedAt());
                }
                // 异常是否因缺少发布时间触发
                if (e.hasErrorCode(ErrorCodes.ISSUED_AT_MISSING)) {
                    System.out.println("Issued at missing!");
                }
                // 异常是否因签发者无效触发
                if (e.hasErrorCode(ErrorCodes.ISSUER_INVALID)) {
                    System.out.println("Issuer invalid: " + jwtClaims.getIssuer());
                }
                // 异常是否因缺少签发者触发
                if (e.hasErrorCode(ErrorCodes.ISSUER_MISSING)) {
                    System.out.println("Issuer missing!");
                }
                // 异常是否因 JSON 无效触发
                if (e.hasErrorCode(ErrorCodes.JSON_INVALID)) {
                    System.out.println("Json invalid: " + jwtClaims.toString());
                }
                // 异常是否因缺少 JWT ID 触发
                if (e.hasErrorCode(ErrorCodes.JWT_ID_MISSING)) {
                    System.out.println("JWT ID missing!");
                }
                // 异常是否因 JwtClaims 格式错误触发
                if (e.hasErrorCode(ErrorCodes.MALFORMED_CLAIM)) {
                    System.out.println("Malformed claim!");
                }
                // 异常是否因缺少生效时间触发
                if (e.hasErrorCode(ErrorCodes.NOT_BEFORE_MISSING)) {
                    System.out.println("Not before missing!");
                }
                // 异常是否因 Token 尚未生效触发
                if (e.hasErrorCode(ErrorCodes.NOT_YET_VALID)) {
                    System.out.println("Not yet valid: " + jwtClaims.getNotBefore());
                }
                // 异常是否因 Token 的 Signature 部分无效触发
                if (e.hasErrorCode(ErrorCodes.SIGNATURE_INVALID)) {
                    System.out.println("Signature invalid: " + jwtClaims.toString());
                }
                // 异常是否因 Token 的 Signature 部分缺失触发
                if (e.hasErrorCode(ErrorCodes.SIGNATURE_MISSING)) {
                    System.out.println("Signature missing!");
                }
                // 异常是否因 Subject 无效触发
                if (e.hasErrorCode(ErrorCodes.SUBJECT_INVALID)) {
                    System.out.println("Subject invalid: " + jwtClaims.getSubject());
                }
                // 异常是否因 Subject 缺失触发
                if (e.hasErrorCode(ErrorCodes.SUBJECT_MISSING)) {
                    System.out.println("Subject missing!");
                }
                // 异常是否因 Type 无效触发
                if (e.hasErrorCode(ErrorCodes.TYPE_INVALID)) {
                    System.out.println("Type invalid: " + jwtClaims.getRawJson());
                }
                // 异常是否因 Type 缺失触发
                if (e.hasErrorCode(ErrorCodes.TYPE_MISSING)) {
                    System.out.println("Type missing!");
                }
            } catch (MalformedClaimException e1) {
                System.out.println("Malformed claim: " + e);
            }
        }
    }
    
    1. 运行结果
    JWT validation succeeded! JwtClaims: JWT Claims Set:{iss=Issuer, exp=1586955295, sub=Subject, aud=[Audience X, Audience Y, Audience Z], nbf=1586954575, iat=1586954695, jti=RFemW0AVr2hTVKMB_d4mpA, email=email@example.com}
    JWT validation succeeded! JwtClaims: JWT Claims Set:{iss=Issuer, exp=1586955296, sub=Subject, aud=[Audience X, Audience Y, Audience Z], nbf=1586954576, iat=1586954696, jti=yRrurCOyzPsi_iezRSeM_Q, email=email@example.com}
    

    相关文章

      网友评论

          本文标题:jose.4.j JWT

          本文链接:https://www.haomeiwen.com/subject/sdyhvhtx.html