keycloak提供了jwks服务,其地址可以在/auth/realms/fabao/.well-known/openid-configuration
的返回结果中找到,jwks_uri
它表示了公钥的颁发者,可以使用颁发出来的公钥来验证token的签名,基地址也是固定的/auth/realms/fabao/protocol/openid-connect/certs
。
springboot构建keycloak的token校验服务
依赖包
jwt的解析以来于java-jwt
包,由jwks服务解析依赖于jwks-rsa
包,jwks是什么,可以看这里
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${spring-boot-dependencies.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.keycloak.bom</groupId><artifactId>keycloak-adapter-bom</artifactId><version>14.0.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- keycloak --><dependency><groupId>org.keycloak</groupId><artifactId>keycloak-spring-boot-starter</artifactId></dependency><!-- jwt --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.11.0</version></dependency><dependency><groupId>com.auth0</groupId><artifactId>jwks-rsa</artifactId><version>0.12.0</version></dependency></dependencies>
相关配置
keycloak:realm: fabaoresource: pkulawclient-key-password: c0b7ab8e-485b-4a10-bff8-7c7d3f472096auth-server-url: http://192.168.xx.xx:8080/auth/realms/fabao/protocol/openid-connect/auth
kc:jwk-set-uri: http://192.168.xx.xx:8080/auth/realms/fabao/protocol/openid-connect/certscerts-id: E_6ih35yTLJMieI0vqg9MmTQrJ6RcUSxiXeNdcMaoYk
jwks服务
@Service
public class JwtService {@Value("${kc.jwk-set-uri}")private String jwksUrl;@Value("${kc.certs-id}")private String certsId;@Cacheable(value = "jwkCache")public Jwk getJwk() throws Exception {return new UrlJwkProvider(new URL(jwksUrl)).get(certsId);}
}
校验token
这只是个简单的demo,真实项目中,这种校验的代码应该写在拦截器中,统一进行处理
@GetMapping("/teacher")public HashMap teacher(@RequestHeader("Authorization") String authHeader) {try {DecodedJWT jwt = JWT.decode(authHeader.replace("Bearer", "").trim());// check JWT is validJwk jwk = jwtService.getJwk();Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);algorithm.verify(jwt);// check JWT role is correctList<String> roles = ((List)jwt.getClaim("realm_access").asMap().get("roles"));if(!roles.contains("teacher"))throw new Exception("not a teacher role");// check JWT is still activeDate expiryDate = jwt.getExpiresAt();if(expiryDate.before(new Date()))throw new Exception("token is expired");// all validation passedreturn new HashMap() {{put("role", "teacher");}};} catch (Exception e) {logger.error("exception : {} ", e.getMessage());return new HashMap() {{put("status", "forbidden");}};}
}
JWKS(JSON Web Key Set)是一个包含一组公钥的 JSON 格式文件,用于在使用 JSON Web Token(JWT)进行身份验证和授权时,验证 JWT 的签名。JWKS 通常用于在 OAuth 2.0 和 OpenID Connect 等认证协议中进行密钥管理。
在 JWKS 中,每个公钥都包含了算法、公钥类型和实际的公钥值。通过 JWKS,验证方可以获取到签发方使用的公钥,从而验证 JWT 的签名是否有效。
JWKS 包含以下主要字段:
keys
:一个数组,包含多个公钥信息的对象,每个对象包括了公钥的相关信息,如算法、公钥类型和公钥值等。
示例 JWKS 文件如下所示:
{"keys": [{"kty": "RSA","kid": "123","use": "sig","alg": "RS256","n": "public_key_value","e": "AQAB"}]
}
在使用 JWT 进行身份验证时,验证方可以通过获取并解析 JWKS 文件中的公钥信息,然后使用这些公钥来验证 JWT 的签名是否有效。这样可以提高安全性,并确保只有合法的签发方才能生成有效的 JWT。
参考:https://developers.redhat.com/blog/authentication-and-authorization-using-the-keycloak-rest-api#keycloak_connection_using_a_java_application