使用java-jwt验证KeyCloak生成的JWT Access Token签名报错如何解决
问题原因
- RS256属于非对称RSA加密算法,签名验证需要使用
RSAPublicKey类型的公钥实例,你代码中直接把公钥字符串封装为SecretKeySpec(该类仅适用于HS256等对称加密算法的密钥场景),因此触发参数校验不通过的报错。 - 你提到使用com.auth0的java-jwt库,但当前代码里用到的
DefaultJwtSignatureValidator、SignatureAlgorithm属于io.jsonwebtoken(jjwt)库的类,两个库的API不通用,下面提供两种场景的修复方案。
方案1:基于当前jjwt库的修复
首先处理从Keycloak拿到的公钥字符串,去掉首尾的-----BEGIN PUBLIC KEY-----、-----END PUBLIC KEY-----标记和所有换行、空格,再转换为RSAPublicKey实例即可,修正后代码如下:
import java.security.KeyFactory; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator; import java.security.interfaces.RSAPublicKey; // 你的原有变量 String publicKeyStr = "<public_key from http://<ip>:<port>/auth/realms/<app> >"; String accessToken = "<valid access token from keycloak>"; String[] chunks = accessToken.split("\\."); // 清理并转换公钥为RSAPublicKey实例 publicKeyStr = publicKeyStr.replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec); // 替换密钥参数后执行验证 SignatureAlgorithm sa = SignatureAlgorithm.RS256; String tokenWithoutSignature = chunks[0] + "." + chunks[1]; DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, rsaPublicKey); if (!validator.isValid(tokenWithoutSignature, chunks[2])) { System.out.println("=============== Invalid access token"); } else { System.out.println("============= valid access token"); }
方案2:切换为com.auth0 java-jwt库的验证写法
如果要使用你提到的com.auth0的java-jwt库,可直接用原生API完成验证,不需要手动拆分JWT分段,代码如下:
import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.JWTVerifier; import java.security.KeyFactory; import java.security.interfaces.RSAPublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; // 公钥转换逻辑和上面一致 String publicKeyStr = "<public_key from http://<ip>:<port>/auth/realms/<app> >"; publicKeyStr = publicKeyStr.replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec); // auth0库原生验证逻辑 String accessToken = "<valid access token from keycloak>"; try { Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null); JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(accessToken); System.out.println("============= valid access token"); } catch (JWTVerificationException exception) { System.out.println("=============== Invalid access token"); }
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

