Oauth2系列9:JWT令牌各种实现

传送门

Oauth2系列1:初识Oauth2

Oauth2系列2:授权码模式

Oauth2系列3:接入前准备

Oauth2系列4:密码模式

Oauth2系列5:客户端凭据模式

Oauth2系列6:隐式模式

Oauth2系列7:授权码和访问令牌的颁发流程是怎样实现的?

Oauth2系列8:何谓JWT令牌?

什么是JWT令牌

官方网站地址:JSON Web Tokens - jwt.io

在上一节里面,讨论过jwt令牌。里面给出了各种语言及相对的多种实现。所以这里面就仔细看一下java版本的实现!

JAVA版本的实现

官方版本给出了7个库实现,分别是

  • java-jwt
  • jose4j
  • nimbus-jose-jwt
  • jjwt-root
  • fusionauth-jwt
  • vertx-auth-jwt
  • inverno-security-jose

具体可以查看https://jwt.io/libraries?language=Java

jose4j

jose是比较流行的java库,可以先看下一下它的Wiki(机译过来的,哈哈)

jose.4.j库是JWT和jose规范套件的开源(Apache2.0)实现。它是用Java编写的,仅依赖于JCA API进行加密。

JSON Web令牌(JWT)是一种简洁、URL安全的方式,用于表示双方之间要传输的声明。JWT是OpenID Connect中的身份令牌格式,它还广泛用于OAuth 2.0和许多其他需要压缩消息安全性的上下文中。

JWT代码示例页面显示了如何使用此库轻松生成和使用JWT。

JOSE是Javascript对象签名和加密的缩写,是开发JSON Web签名(JWS)、JSON Web加密(JWE)和JSON Web密钥(JWK)规范的IETF工作组。JWS和JWE使用JSON和base64url编码以(相对)简单、紧凑和web安全的格式保护消息,而JWK定义了加密密钥的JSON表示。JWS、JWE和JWK的实际算法在JSON Web算法(JWA)中定义。

获取库版本

以上一个工程为例,所有的实现都放在auth认证的jwt包下面,引入pom坐标,0.9.0为最新版本

<dependency>
    <groupId>org.bitbucket.b_c</groupId>
    <artifactId>jose4j</artifactId>
    <version>0.9.0</version>
  </dependency>

快速生成与验证jwt

生成密钥对

/**
     * 创建密钥对
     * @return
     * @throws JoseException
     */
    public static RsaJsonWebKey genRsaJsonWebKey() throws JoseException
    {
        // 生成一个RSA密钥对,该密钥对将用于JWT的签名和验证,包装在JWK中
        RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
        
        // 给JWK一个密钥ID
        rsaJsonWebKey.setKeyId("k1");
        
        return rsaJsonWebKey;
    }

生成jwt令牌

/**
     * t生成jwt令牌
     * @return
     * @throws JoseException
     */
    public static String producing(RsaJsonWebKey rsaJsonWebKey) throws JoseException
    {
        
        // 创建声明,也就是JWT的内容
        JwtClaims claims = new JwtClaims();
        // 创建令牌并签名的人
        claims.setIssuer("Issuer");
        // 令牌将发送给谁
        claims.setAudience("Audience");
        // 令牌到期时间(10分钟后)
        claims.setExpirationTimeMinutesInTheFuture(10);
        // 令牌的唯一标识符
        claims.setGeneratedJwtId();
        // 颁发/创建令牌的时间(现在)
        claims.setIssuedAtToNow();
        // 令牌尚未生效的时间(2分钟前)
        claims.setNotBeforeMinutesInThePast(2);
        // 主题/主体是关于谁的令牌
        claims.setSubject("subject");
        // 可以添加有关主题的其他声明/属性
        claims.setClaim("email", "mail@example.com");
        List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
        // 多值声明也可以工作,最终将成为一个JSON数组
        claims.setStringListClaim("groups", groups);
        
        // JWT是JWS和/或JWE,其中JSON声明为有效负载。
        // 在本例中,它是一个JWS,因此我们创建了一个JsonWebSignature对象。
        JsonWebSignature jws = new JsonWebSignature();
        // JWS的有效负载是JWT声明的JSON内容
        jws.setPayload(claims.toJson());
        // JWT使用私钥签名
        jws.setKey(rsaJsonWebKey.getPrivateKey());
        // 在这个示例中,我们只有一个密钥,但使用密钥ID有助于促进顺利的密钥滚动过程
        jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
        // 在JWT/JWS上设置完整性保护声明的签名算法
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
        
        String jwt = jws.getCompactSerialization();
        return jwt;
    }

消费令牌

/**
     * 验证jwt令牌
     * @param jwt
     * @param key
     * @return
     * @throws MalformedClaimException
     */
    public static JwtClaims consuming(String jwt, Key key) throws MalformedClaimException
    {
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
            // JWT必须有到期时间
            .setRequireExpirationTime()
            // 在验证基于时间的声明以解释时钟偏差时允许一些余地
            .setAllowedClockSkewInSeconds(30)
            // JWT必须有主题声明
            .setRequireSubject()
            // JWT需要由谁签发
            .setExpectedIssuer("Issuer")
            // JWT的目标用户
            .setExpectedAudience("Audience")
            // 使用公钥验证签名
            .setVerificationKey(key)
            // 仅允许给定上下文中的预期签名算法
            .setJwsAlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256)
            .build();
        
        try
        {
            // 验证JWT并将其处理至索赔
            JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
            return jwtClaims;
        }
        catch (InvalidJwtException e)
        {
            // 是否过期
            if (e.hasExpired())
            {
                System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
            }
            if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID))
            {
                System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
            }
            
        }
        return null;
    }

写一个main方法测试一下

public static void main(String[] args) throws Exception
    {
        RsaJsonWebKey rsaJsonWebKey = genRsaJsonWebKey();
        String jwt = producing(rsaJsonWebKey);
        JwtClaims jwtClaims = consuming(jwt, rsaJsonWebKey.getKey());
        System.out.println(jwtClaims);
        
    }

 看下打印,从里面可以清晰的看到jwt生成相关的信息,及最后解析出来的JwtClaims,消费方可以直接从里面获取数据了,即表示jwt验证完成

20:37:07.792 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.UnsecuredNoneAlgorithm(none|null) registered for alg algorithm none
20:37:08.046 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.HmacUsingShaAlgorithm$HmacSha256(HS256|HmacSHA256) registered for alg algorithm HS256
20:37:08.046 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.HmacUsingShaAlgorithm$HmacSha384(HS384|HmacSHA384) registered for alg algorithm HS384
20:37:08.047 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.HmacUsingShaAlgorithm$HmacSha512(HS512|HmacSHA512) registered for alg algorithm HS512
20:37:08.050 [main] DEBUG org.jose4j.jws.EdDsaAlgorithm - EdDSA via EdDSA is NOT available from the underlying JCE (org.jose4j.lang.JoseException: Unable to get an implementation of algorithm name: EdDSA; caused by: java.security.NoSuchAlgorithmException: EdDSA Signature not available).
20:37:08.050 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - EdDSA is unavailable so will not be registered for alg algorithms.
20:37:08.052 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaP256UsingSha256(ES256|SHA256withECDSA) registered for alg algorithm ES256
20:37:08.054 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaP384UsingSha384(ES384|SHA384withECDSA) registered for alg algorithm ES384
20:37:08.055 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaP521UsingSha512(ES512|SHA512withECDSA) registered for alg algorithm ES512
20:37:08.070 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaSECP256K1UsingSha256(ES256K|SHA256withECDSA) registered for alg algorithm ES256K
20:37:08.071 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaSha256(RS256|SHA256withRSA) registered for alg algorithm RS256
20:37:08.071 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaSha384(RS384|SHA384withRSA) registered for alg algorithm RS384
20:37:08.072 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaSha512(RS512|SHA512withRSA) registered for alg algorithm RS512
20:37:08.073 [main] DEBUG org.jose4j.jws.RsaUsingShaAlgorithm$RsaPssSha256 - PS256 via SHA256withRSAandMGF1 is NOT available from the underlying JCE (org.jose4j.lang.JoseException: Unable to get an implementation of algorithm name: SHA256withRSAandMGF1; caused by: java.security.NoSuchAlgorithmException: SHA256withRSAandMGF1 Signature not available).
20:37:08.073 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - PS256 is unavailable so will not be registered for alg algorithms.
20:37:08.074 [main] DEBUG org.jose4j.jws.RsaUsingShaAlgorithm$RsaPssSha384 - PS384 via SHA384withRSAandMGF1 is NOT available from the underlying JCE (org.jose4j.lang.JoseException: Unable to get an implementation of algorithm name: SHA384withRSAandMGF1; caused by: java.security.NoSuchAlgorithmException: SHA384withRSAandMGF1 Signature not available).
20:37:08.074 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - PS384 is unavailable so will not be registered for alg algorithms.
20:37:08.075 [main] DEBUG org.jose4j.jws.RsaUsingShaAlgorithm$RsaPssSha512 - PS512 via SHA512withRSAandMGF1 is NOT available from the underlying JCE (org.jose4j.lang.JoseException: Unable to get an implementation of algorithm name: SHA512withRSAandMGF1; caused by: java.security.NoSuchAlgorithmException: SHA512withRSAandMGF1 Signature not available).
20:37:08.075 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - PS512 is unavailable so will not be registered for alg algorithms.
20:37:08.075 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWS signature algorithms: [none, HS256, HS384, HS512, ES256, ES384, ES512, ES256K, RS256, RS384, RS512]
20:37:08.079 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.RsaKeyManagementAlgorithm$Rsa1_5(RSA1_5|RSA/ECB/PKCS1Padding) registered for alg algorithm RSA1_5
20:37:08.079 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.RsaKeyManagementAlgorithm$RsaOaep(RSA-OAEP|RSA/ECB/OAEPWithSHA-1AndMGF1Padding) registered for alg algorithm RSA-OAEP
20:37:08.100 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.RsaKeyManagementAlgorithm$RsaOaep256(RSA-OAEP-256|RSA/ECB/OAEPWithSHA-256AndMGF1Padding) registered for alg algorithm RSA-OAEP-256
20:37:08.100 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.DirectKeyManagementAlgorithm(dir|null) registered for alg algorithm dir
20:37:08.102 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesKeyWrapManagementAlgorithm$Aes128(A128KW|AESWrap) registered for alg algorithm A128KW
20:37:08.103 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AESWrap is 128
20:37:08.103 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - A192KW is unavailable so will not be registered for alg algorithms.
20:37:08.103 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AESWrap is 128
20:37:08.104 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - A256KW is unavailable so will not be registered for alg algorithms.
20:37:08.107 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.EcdhKeyAgreementAlgorithm(ECDH-ES|ECDH) registered for alg algorithm ECDH-ES
20:37:08.108 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.EcdhKeyAgreementWithAesKeyWrapAlgorithm$EcdhKeyAgreementWithAes128KeyWrapAlgorithm(ECDH-ES+A128KW|N/A) registered for alg algorithm ECDH-ES+A128KW
20:37:08.110 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AESWrap is 128
20:37:08.110 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - ECDH-ES+A192KW is unavailable so will not be registered for alg algorithms.
20:37:08.112 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AESWrap is 128
20:37:08.112 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - ECDH-ES+A256KW is unavailable so will not be registered for alg algorithms.
20:37:08.114 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.Pbes2HmacShaWithAesKeyWrapAlgorithm$HmacSha256Aes128(PBES2-HS256+A128KW|n/a) registered for alg algorithm PBES2-HS256+A128KW
20:37:08.114 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AESWrap is 128
20:37:08.114 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - PBES2-HS384+A192KW is unavailable so will not be registered for alg algorithms.
20:37:08.115 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AESWrap is 128
20:37:08.115 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - PBES2-HS512+A256KW is unavailable so will not be registered for alg algorithms.
20:37:08.138 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesGcmKeyEncryptionAlgorithm$Aes128Gcm(A128GCMKW|AES/GCM/NoPadding) registered for alg algorithm A128GCMKW
20:37:08.139 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AES/GCM/NoPadding is 128
20:37:08.139 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - A192GCMKW is unavailable so will not be registered for alg algorithms.
20:37:08.140 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AES/GCM/NoPadding is 128
20:37:08.140 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - A256GCMKW is unavailable so will not be registered for alg algorithms.
20:37:08.140 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWE key management algorithms: [RSA1_5, RSA-OAEP, RSA-OAEP-256, dir, A128KW, ECDH-ES, ECDH-ES+A128KW, PBES2-HS256+A128KW, A128GCMKW]
20:37:08.143 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesCbcHmacSha2ContentEncryptionAlgorithm$Aes128CbcHmacSha256(A128CBC-HS256|AES/CBC/PKCS5Padding) registered for enc algorithm A128CBC-HS256
20:37:08.144 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AES/CBC/PKCS5Padding is 128
20:37:08.144 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - A192CBC-HS384 is unavailable so will not be registered for enc algorithms.
20:37:08.144 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AES/CBC/PKCS5Padding is 128
20:37:08.145 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - A256CBC-HS512 is unavailable so will not be registered for enc algorithms.
20:37:08.146 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesGcmContentEncryptionAlgorithm$Aes128Gcm(A128GCM|AES/GCM/NoPadding) registered for enc algorithm A128GCM
20:37:08.147 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AES/GCM/NoPadding is 128
20:37:08.147 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - A192GCM is unavailable so will not be registered for enc algorithms.
20:37:08.147 [main] DEBUG org.jose4j.jwe.CipherStrengthSupport - max allowed key length for AES/GCM/NoPadding is 128
20:37:08.147 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - A256GCM is unavailable so will not be registered for enc algorithms.
20:37:08.148 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWE content encryption algorithms: [A128CBC-HS256, A128GCM]
20:37:08.149 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->CompressionAlgorithm - org.jose4j.zip.DeflateRFC1951CompressionAlgorithm@7f9fcf7f registered for zip algorithm DEF
20:37:08.149 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWE compression algorithms: [DEF]
20:37:08.149 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - Initialized jose4j in 359ms
JWT Claims Set:{iss=Issuer, aud=Audience, exp=1665146827, jti=lqn2i0wvh4xkGJlWxq3-AA, iat=1665146227, nbf=1665146107, sub=subject, email=mail@example.com, groups=[group-one, other-group, group-three]}

如果是同一个应用,公私钥都在一块,刚才的代码是没有什么问题的。如果是授权系统颁发给其它系统验证,那理论上公私钥是分开的,有一个下发的过程,即提前生成好密钥对,下发公钥给应用系统,授权服务持有私钥,上面的代码可以改造如下:

/**
     * t生成jwt令牌
     * @return
     * @throws JoseException
     */
    public static String producing(String privateKey, String keyId) throws JoseException
    {
        
        // 创建声明,也就是JWT的内容
        JwtClaims claims = new JwtClaims();
        // 创建令牌并签名的人
        claims.setIssuer("Issuer");
        // 令牌将发送给谁
        claims.setAudience("Audience");
        // 令牌到期时间(10分钟后)
        claims.setExpirationTimeMinutesInTheFuture(10);
        // 令牌的唯一标识符
        claims.setGeneratedJwtId();
        // 颁发/创建令牌的时间(现在)
        claims.setIssuedAtToNow();
        // 令牌尚未生效的时间(2分钟前)
        claims.setNotBeforeMinutesInThePast(2);
        // 主题/主体是关于谁的令牌
        claims.setSubject("subject");
        // 可以添加有关主题的其他声明/属性
        claims.setClaim("email", "mail@example.com");
        List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
        // 多值声明也可以工作,最终将成为一个JSON数组
        claims.setStringListClaim("groups", groups);
        
        // JWT是JWS和/或JWE,其中JSON声明为有效负载。
        // 在本例中,它是一个JWS,因此我们创建了一个JsonWebSignature对象。
        JsonWebSignature jws = new JsonWebSignature();
        // JWS的有效负载是JWT声明的JSON内容
        jws.setPayload(claims.toJson());
        // JWT使用私钥签名
        jws.setKey(new RsaJsonWebKey(JsonUtil.parseJson(privateKey)).getPrivateKey());
        // 在这个示例中,我们只有一个密钥,但使用密钥ID有助于促进顺利的密钥滚动过程
        jws.setKeyIdHeaderValue(keyId);
        // 在JWT/JWS上设置完整性保护声明的签名算法
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
        
        String jwt = jws.getCompactSerialization();
        return jwt;
    }
    
    /**
     * 验证jwt令牌
     * @param jwt
     * @param publicKey
     * @return
     * @throws MalformedClaimException
     */
    public static JwtClaims consuming(String jwt, String publicKey) throws MalformedClaimException, JoseException
    {
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
            // JWT必须有到期时间
            .setRequireExpirationTime()
            // 在验证基于时间的声明以解释时钟偏差时允许一些余地
            .setAllowedClockSkewInSeconds(30)
            // JWT必须有主题声明
            .setRequireSubject()
            // JWT需要由谁签发
            .setExpectedIssuer("Issuer")
            // JWT的目标用户
            .setExpectedAudience("Audience")
            // 使用公钥验证签名
            .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKey)).getPublicKey())
            // 仅允许给定上下文中的预期签名算法
            .setJwsAlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256)
            .build();
        
        try
        {
            // 验证JWT并将其处理至索赔
            JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
            return jwtClaims;
        }
        catch (InvalidJwtException e)
        {
            System.err.println("Invalid JWT! " + e);
            
            // 是否过期
            if (e.hasExpired())
            {
                System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
            }
            if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID))
            {
                System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
            }
            
        }
        return null;
    }
    
    public static void main(String[] args) throws Exception
    {
        RsaJsonWebKey rsaJsonWebKey = genRsaJsonWebKey();
        String publicKey = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
//        System.out.println(publicKey);

        String privateKey = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
//        System.out.println(privateKey);
        String jwt = producing(privateKey, rsaJsonWebKey.getKeyId());
        JwtClaims jwtClaims = consuming(jwt, publicKey);
        System.out.println(jwtClaims);


    }

Java JWT

Java JWT是JSON Web令牌(JWT)的Java实现-RFC 7519

它还支持Android版本的库, JWTDecode.Android

JDK版本

Java 8、11和17支持此库

获取库版本

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

创建并签署令牌

首先需要通过调用JWT.create()来创建JWTCreator实例。使用生成器定义令牌需要的自定义声明。最后,获取String标记call sign()并传递Algorithm实例。

对称加密

可以选择用对称的加密算法HS256来签名令牌,如下,key为"secret",内容为"auth0"

/**
     * 用HS256算法创建token
     * @return
     */
    public static String genHsToken()
    {
        Algorithm algorithm = Algorithm.HMAC256("secret");
        String token = JWT.create().withIssuer("auth0").sign(algorithm);
        
        return token;
    }

可以写一个main方法,打印一下生成的token,类似如下格式

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.izVguZPRsBQ5Rqw6dhMvcIwy8_9lQnrO3vpxGwPCuzs

把它放到jwt工具里面,会展示对应的信息,header+payload+sign

 检验令牌

首先需要通过调用JWT.require()创建JWTVerifier实例,并传递Algorithm实例。如果您需要令牌具有特定的Claim值,请使用生成器来定义它们。方法build()返回的实例是可重用的,因此您可以定义它一次,然后使用它来验证不同的令牌。最后调用验证器verifier.verify()传递令牌。

 public static void verifyToken(String token)
    {
        Algorithm algorithm = Algorithm.HMAC256("secret");
        JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer("auth0").build();
        jwtVerifier.verify(token);
    }

这里用的密钥都是一样的为"secret",如果令牌检验不过,会抛出异常,比如密钥不对

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot Security OAuth2是基于Spring Security和OAuth2的框架,用于实现授权服务器和资源服务器。JWT令牌是一个基于JSON的开放标准,用于在各方之间安全地传输信息。 在Spring Boot Security OAuth2中实现支持JWT令牌的授权服务器,可以按照以下步骤进行: 1. 添加依赖:在项目的pom.xml文件中添加Spring Security OAuth2和JWT的相关依赖。 2. 配置授权服务器:在Spring Boot应用程序的配置文件中,配置授权服务器的基本设置,包括端点URL、客户端信息、用户认证信息等。 3. 配置JWT令牌:配置JWT令牌的签名密钥和过期时间等信息。可以使用开源库如jjwt来生成和验证JWT令牌。 4. 创建自定义的认证提供程序:实现自定义的认证提供程序来支持JWT令牌的认证机制。在认证提供程序中,可以使用JWT令牌解析并验证请求中的令牌信息。 5. 创建自定义的用户详细信息服务:实现自定义的用户详细信息服务,用于从数据库或其他存储中获取用户的详细信息。在用户详细信息服务中,可以根据JWT令牌中的信息获取用户信息。 6. 配置授权服务器的访问规则:配置授权服务器的访问规则,包括允许或禁止特定角色或权限的访问。 7. 测试访问授权服务器:使用客户端应用程序发送请求到授权服务器的端点,获取JWT令牌并验证其有效性。 通过以上步骤,可以实现一个支持JWT令牌的授权服务器。该服务器可以提供为客户端应用程序颁发和验证JWT令牌的功能,以实现安全并可靠的用户认证和授权控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值