[英]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.