如何设置公共密钥以从Keycloak验证JWT令牌?

时间:2018-11-29 16:07:22

标签: jwt rsa keycloak

我正在编写后端微服务,该服务从前端接收具有Authorisation: Bearer ...标头的请求,并从keycloak(位于docker容器内)获取令牌。

我从领域设置的“密钥”部分获得了RSA公共密钥来验证该令牌的签名,但是似乎当带有keycloak的容器重新启动时,它会重新生成一对密钥,并且在服务配置中设置的公共密钥变得无效。

从keycloak使用RSA公钥的正确方法是什么?是否可以通过某种方式将其配置为使用固定的密钥对进行领域设置?领域导出时是否导出密钥?或者,我必须使用http://keycloak:8080/auth/realms/:realm_name:之类的网址从keycloak获取公钥,我宁愿不这样做,因为这会在keycloak和后端之间增加一个依赖关系。

1 个答案:

答案 0 :(得分:0)

您应基于发行者身份服务器的/.well-known/jwks端点来验证JWT令牌的签名。

1)查询发行者身份服务器的/.well-known/jwks端点(JWKS代表JSON Web密钥集)

2)从JWKS中获得与我们正在验证的kid令牌相同的Bearer(密钥ID)的JWK(JSON Web密钥)。要从您的JWT令牌中获取kid,请先使用jwt.io的调试器工具对其进行解码。

3)只要使用非对称密码算法(例如:RS256)验证身份服务器颁发的令牌,我们就只能使用公钥来验证签名(因此您不需要私钥)

4)可以从JWK检索公共密钥(它是JWK JSON中的x5c条目)

5)使用此公钥验证JWT Bearer令牌的签名。

例如,在Java中,您可以像这样进行验证:

// verify JWT signature based on Access Identity's JWKS RSA public key (RS256)
try {

  Jwk jwk = new UrlJwkProvider(new URL(issuer + Constants.JWKS_ENDPOINT)).get(decodedJWT.getKeyId());
  final PublicKey publicKey = jwk.getPublicKey();

  if (!(publicKey instanceof RSAPublicKey)) {
    throw new IllegalArgumentException("Key with ID " + decodedJWT.getKeyId() + " was found in JWKS but is not a RSA-key.");
  }

  Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null);
  JWTVerifier verifier = JWT.require(algorithm)
      .withIssuer(issuer)
      .build(); //Reusable verifier instance

  verifier.verify(bearerToken);

  LOGGER.info("Token verified!");

} catch (Exception e) {
  LOGGER.error(e.getMessage());
  throw new InvalidAccessTokenException("JWTVerificationException - Invalid token signature.");
}