如何使用 java 验证由 keycloak 签名的 JWT

Posted

技术标签:

【中文标题】如何使用 java 验证由 keycloak 签名的 JWT【英文标题】:How to verify JWT signed by keycloak using java 【发布时间】:2020-11-26 13:17:00 【问题描述】:

我有 keyCloack 生成的 JWT,类似这样的 RS256

样本:

eyJhbGciOwia2lkIiA6ICJtSG1lajZEc09GaV9MejdSMjhzWjdMWkxBRXVzIn0.eyBzA2MzQvOTcwNjM1L1NNUyIsIi83Ni83NS9TTVMiXSwicHJYW1lIjoidGVzdDEwNUB1c2VyLmNvbSIsInVzZXJOYW1lIjoidGVzdDEwNUB1c2VyLmNvbSIsInVzZXJJZCI6IjU4NDM2NmQ4LWU5NDItNGJhNy04OGVlLWMyZTBlODhmZmY5ZCIsImVtYWlsIjoidGVzdDEwNUB1c2VyLmNvbSJ9.4TgC1MLyUl1P36oD6FafBCh0peEaCBmkyLheVjnlBu8uePl9xgEN6wdeWe

我需要使用 keycloack 证书解码和验证这个令牌。

我可以通过 api 访问 keycloack 证书。

https://xxx.xxx.com.tr/auth/realms/myrealm/protocol/openid-connect/certs

在响应中,我有 x5c 字段。


    "keys": [
        
            "kid": "j6DsCpPOz1RXJhtPR28sZ7LZLAEus",
            "kty": "RSA",
            "alg": "RS256",
            "use": "sig",
            "n": "m0oTFvyLhLGIciXfndxc7uhIKE2-q9nJQKByd0FVYe8Cd4CHDpTzzcYdPWRR-1_VKQ75wqpybRt-LnnTKPNCXrPtPDRn2GFihtYyyO8VjeVtnz-iYJJAHkdp25HlMtX9l-VjnQX9s70-lbMmCVCRTerw",
            "e": "AQAB",
            "x5c": [
                "MIICnTCCAYUCBgFzh2ZkQzANBgkqhkiG9w0BAQ50F/bO9PpWzJglQkU3q8CAwEAATANBgkqhkiG9w01faO/9ZzyiLMLsorUKzYPNAxc7Q9rLE0J2MCWfapx3/E4yyNjISuB1HpS5iF44OEhGHJlw7JQeogcZat0enB8yyXtP/cgBhCnrWwfugX8rHsWfHakBGdsoazR9w=="
            ],
            "x5t": "YF6LE97opzsTtD-yLNx9-Lo",
            "x5t#S256": "SdNCfMbCjvcq-JY3iiGAj7De9Hal_0Cck-bDFK3Ow"
        
    ]

如果我将 x5c 部分放在 ----CERTIFICATE----- 标签中,我可以通过 https://jwt.io/ 验证这个 jwt

-----BEGIN CERTIFICATE-----
MIICnTCCAYUCBgFzh2ZkQzANBgkqhkiG9w0BAQ50F/bO9PpWzJglQkU3q8CAwEAATANBgkqhkiG9w01faO/9ZzyiLMLsorUKzYPNAxc7Q9rLE0J2MCWfapx3/E4yyNjISuB1HpS5iF44OEhGHJlw7JQeogcZat0enB8yyXtP/cgBhCnrWwfugX8rHsWfHakBGdsoazR9w==
-----END CERTIFICATE----- 

如何在 Java 中验证相同的内容?

我尝试了几件事,但都失败了。

【问题讨论】:

这能回答你的问题吗? Verifying JWT Signature using public key endpoint @jps 不是我在下面添加了解决方案 如果您正在向 keycloak 编写插件,使用 AppAuthManager 可以帮助您验证令牌。这不是您正在寻找的,但可能会对某人有所帮助。 【参考方案1】:

我找到了路。

首先,我们需要登录keycloack控制台,你可以得到与realm相关的公钥。

现在您拥有来自 keycloak 的公钥和来自用户的 JWT。

需要先导入相关库。

<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>

接下来就是逻辑了。

String token = "ghfghfdhdhdfhdfghdhdfhdfhdfhhdf";
String rsaPublicKey = "awdasdsadaefafafaef5df65d4f";
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(rsaPublicKey));
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey = kf.generatePublic(keySpec);

我们有公钥,我们需要验证它

Jws<Claims> jwt = null;
try 
    jwt = Jwts.parserBuilder()
                        .setSigningKey(publicKey)
                        .build()
                        .parseClaimsJws(token);
             catch (Exception e) 
                // if you get error, that means token is invalid.
            

【讨论】:

【参考方案2】:

我认为您可以为此使用 jose4j 库。

HttpsJwks httpsJkws = new HttpsJwks("https://xxx.xxx.com.tr/auth/realms/myrealm/protocol/openid-connect/certs");
HttpsJwksVerificationKeyResolver httpsJwksKeyResolver = new HttpsJwksVerificationKeyResolver(httpsJkws);


// Use JwtConsumerBuilder to construct an appropriate JwtConsumer, which will
// be used to validate and process the JWT. But, in this case, provide it with
// the HttpsJwksVerificationKeyResolver instance rather than setting the
// verification key explicitly.

jwtConsumer = new JwtConsumerBuilder()
    // ... other set up of the JwtConsumerBuilder ...
    .setVerificationKeyResolver(httpsJwksKeyResolver)
    // ...
    .build();

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review @Carsten Løvbo Andersen 你是绝对正确的。我编辑了我的答案。

以上是关于如何使用 java 验证由 keycloak 签名的 JWT的主要内容,如果未能解决你的问题,请参考以下文章

使用 java-jwt 验证访问令牌签名

http://jwt.io 如何检索 keycloak 签名密钥?

验证openssl c ++中的签名,该签名由JAVA DSA签名?

我应该明确验证 Keycloak 令牌还是由 Keycloak 适配器完成?

在 Spring Boot 中使用 Keycloak 实现 JWT、JWE 和 JWS(签名 JWT)

如何在 Keycloak 身份验证之前调用 javax.servlet.Filter