[英]Spring Oauth2 Authorization Server - How to use multiples JWK Keys
我在我的系统中有一个要求,在某些流程中我必须使用 JWT 和特定的私钥/公钥,而其他流程必须使用另一个 JWT 和其他密钥。
我正在使用 spring oauth2 授权服务器 1.0.0。
当我尝试设置两个密钥时,生成 jwks 端点可以正常工作,但是当我执行 POST /oauth2/token 时,出现以下异常:
org.springframework.security.oauth2.jwt.JwtEncodingException: An error occurred while attempting to encode the Jwt: Found multiple JWK signing keys for algorithm 'RS256'
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.selectJwk(NimbusJwtEncoder.java:128) ~[spring-security-oauth2-jose-6.0.0.jar:6.0.0]
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.encode(NimbusJwtEncoder.java:108) ~[spring-security-oauth2-jose-6.0.0.jar:6.0.0]
at org.springframework.security.oauth2.server.authorization.token.JwtGenerator.generate(JwtGenerator.java:159) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]
at org.springframework.security.oauth2.server.authorization.token.JwtGenerator.generate(JwtGenerator.java:58) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]
at org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator.generate(DelegatingOAuth2TokenGenerator.java:59) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]
at org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider.authenticate(OAuth2ClientCredentialsAuthenticationProvider.java:125) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-6.0.0.jar:6.0.0]
at org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter.doFilterInternal(OAuth2TokenEndpointFilter.java:167) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.2.jar:6.0.2]
我在同一个授权服务器中必须使用 JWK 密钥的概念可以吗?
我如何实现授权服务器在特定的客户端凭据中使用一个 JWK 密钥,并在另一个客户端凭据中请求另一个 JWK 密钥?
我的代码:
@EnableWebSecurity
@Configuration
@Slf4j
public class AuthSecurityConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
}
@Bean
public SecurityFilterChain authFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
return http.formLogin(Customizer.withDefaults()).build();
}
public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder
,JdbcTemplate jdbcTemplate
) {
JdbcRegisteredClientRepository clientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
return clientRepository;
}
@Bean
public OAuth2AuthorizationService auth2AuthorizationService(JdbcOperations jdbcOperations,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(
jdbcOperations,
registeredClientRepository
);
}
@Bean
public OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService(JdbcOperations jdbcOperations,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(
jdbcOperations,
registeredClientRepository
);
}
@Bean
public JWKSet jwkSet(AuthProperties authProperties) throws Exception {
List<JWK> keys = new ArrayList<>();
for (JksProperties jwk : authProperties.getJksList()) {
keys.add(loadRsa(jwk));
}
return new JWKSet(keys);
}
@Bean
public JWKSource<SecurityContext> jwkSource(JWKSet jwkSet) {
return ((jwkSelector, securityContext) -> jwkSelector.select(jwkSet));
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
我不确定我是否完全理解RFC-7517 ,但它似乎允许有多个密钥,没有特别限制。
我很惊讶,因为从解码方面来看,我希望从 JWKS 端点获取使用给定算法解码 JWT的密钥:依次尝试多个密钥直到一个有效......
您是否考虑过运行多个授权服务器实例,每个实例都有一个特定的密钥? 我想您的客户会知道要联系哪个授权服务器,但资源服务器需要多租户(接受多个发行者颁发的身份)。 您必须为此提供JwtIssuerAuthenticationManagerResolver
bean:
http.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver));
我已经围绕spring-boot-starter-oauth2-resource-server
编写了薄包装器,它支持这种场景,仅从属性文件进行配置(上面的 bean 是自动提供的):
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- replace "webmvc" with "weblux" if your app is reactive -->
<!-- replace "jwt" with "introspecting" to use token introspection instead of JWT decoding -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<!-- this version is to be used with spring-boot 3.0.0, use 5.x for spring-boot 2.6.x or before -->
<version>6.0.7</version>
</dependency>
@Configuration
@EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443
com.c4-soft.springaddons.security.issuers[0].authorities.claims=groups,roles
com.c4-soft.springaddons.security.issuers[1].location=https://localhost:8444
com.c4-soft.springaddons.security.issuers[1].authorities.claims=groups,roles
com.c4-soft.springaddons.security.cors[0].path=/some-api
OAuth 有一个内置的机制来管理多个密钥,称为密钥标识符 (kid)。 这为授权服务器提供了一种自动更新令牌签名密钥的方法,其中旧密钥和新密钥都在使用中,但较新的令牌使用较新的密钥颁发。
检查这两件事:
JWKS 必须返回多个具有不同 kid 值的公共 JWK 条目
当向客户端发出 JWT 时,它必须在其 JWT header 中包含一个孩子,该孩子映射到用于验证它的公钥
通常,授权服务器将使您能够配置不同的令牌颁发者,然后您可以将令牌颁发者关联到客户端。 这应该不会对应用程序代码产生影响,因为 JWKS 机制和孩子们应该会处理它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.