英文:
How to scale Authorization server
问题
I use k8s for scale Authorization server (2 replicas) when I get token from authorizationServer1 and check token in resource server by introspect endpoint response 401 but authorizationServer2 get Success.
How to handle it or some guide to right way for scale Authorization server
public class AuthServer {
@Autowired
private OAuthConfig authConfig;
@Autowired
PasswordEncoder passwordEncoder;
@Value("${auth.server.issuer}")
private String issuerAuth;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
return http.build();
}
@Bean
public TokenSettings tokenSettings() {
// @formatter:off
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(30L))
.build();
// @formatter:on
}
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
RegisteredClient registeredClient = registeredClientRepository.findByClientId(authConfig.getClientId());
if (Objects.isNull(registeredClient)) {
registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId(authConfig.getClientId())
.clientSecret(passwordEncoder.encode(authConfig.getClientSecret()))
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.tokenSettings(tokenSettings())
.scope("read")
.build();
}
registeredClientRepository.save(registeredClient);
return registeredClientRepository;
}
@Bean
public JWKSource<SecurityContext> jwkSource() throws Exception {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID("key-id")
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
System.out.println(jwkSet);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(3072);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(issuerAuth).build();
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtEncodingContextOAuth2TokenCustomizer() {
return (context -> {
Authentication authentication = context.getPrincipal();
if (authentication.getPrincipal() instanceof String) {
OAuth2AuthorizationGrantAuthenticationToken tok = (OAuth2AuthorizationGrantAuthenticationToken) context.getAuthorizationGrant();
context.getClaims().claim("merchantCenterId", tok.getAdditionalParameters().get("merchantCenterId"));
context.getClaims().claim("roleId", tok.getAdditionalParameters().get("roleId"));
context.getClaims().claim("username", tok.getAdditionalParameters().get("user_name"));
}
});
}
}
英文:
I use k8s for scale Authorization server (2 replicas) when I get token from authorizationServer1 and check token
in resource server by introspect endpoint response 401 but authorizationServer2 get Success.
How to handle it or some guide to right way for scale Authorization server
public class AuthServer {
@Autowired
private OAuthConfig authConfig;
@Autowired
PasswordEncoder passwordEncoder;
@Value("${auth.server.issuer}")
private String issuerAuth;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
return http.build();
}
@Bean
public TokenSettings tokenSettings() {
// @formatter:off
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(30L))
.build();
// @formatter:on
}
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
RegisteredClient registeredClient = registeredClientRepository.findByClientId(authConfig.getClientId());
if (Objects.isNull(registeredClient)) {
registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId(authConfig.getClientId())
.clientSecret(passwordEncoder.encode(authConfig.getClientSecret()))
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.tokenSettings(tokenSettings())
.scope("read")
.build();
}
registeredClientRepository.save(registeredClient);
return registeredClientRepository;
}
@Bean
public JWKSource<SecurityContext> jwkSource() throws Exception {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID("key-id")
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
System.out.println(jwkSet);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(3072);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(issuerAuth).build();
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtEncodingContextOAuth2TokenCustomizer() {
return (context -> {
Authentication authentication = context.getPrincipal();
if (authentication.getPrincipal() instanceof String) {
OAuth2AuthorizationGrantAuthenticationToken tok = (OAuth2AuthorizationGrantAuthenticationToken) context.getAuthorizationGrant();
context.getClaims().claim("merchantCenterId", tok.getAdditionalParameters().get("merchantCenterId"));
context.getClaims().claim("roleId", tok.getAdditionalParameters().get("roleId"));
context.getClaims().claim("username", tok.getAdditionalParameters().get("user_name"));
}
});
}
}
答案1
得分: 0
只翻译文本部分,不包括代码:
"I'll just echo what Marcus said in comments, which is that you'll need to persist authorizations and authorization consent in a database such as the JDBC implementation of the OAuth2AuthorizationService and the OAuth2AuthorizationConsentService or the guide How-to: Implement core services with JPA demonstrates. Or if you choose, you can store them in a NoSQL data store like Redis, but there is not an out-of-the-box implementation for this.
Note that you should also be storing your sessions and keys in a database or NoSQL data store (such as Redis). Spring Session is great for offloading sessions to a data store and can easily be added to your Spring Boot app, but storing keys outside the JVM has not yet been demonstrated.
For this, I'd encourage you to check out Rob Winch's talk at Spring I/O from May 2023 on Enterprise Security with Spring Authorization Server. Towards the end, his talk covers plugging in a key rotation strategy and he mentions that you can easily replace the in-memory implementation from his sample repo with one backed by a database (using Spring Data JDBC or JPA for example).
These are the tips you'll need to scale your k8s-based authz server in production."
英文:
I'll just echo what Marcus said in comments, which is that you'll need to persist authorizations and authorization consent in a database such as the JDBC implementation of the OAuth2AuthorizationService and the OAuth2AuthorizationConsentService or the guide How-to: Implement core services with JPA demonstrates. Or if you choose, you can store them in a NoSQL data store like Redis, but there is not an out-of-the-box implementation for this.
Note that you should also be storing your sessions and keys in a database or NoSQL data store (such as Redis). Spring Session is great for offloading sessions to a data store and can easily be added to your Spring Boot app, but storing keys outside the JVM has not yet been demonstrated.
For this, I'd encourage you to check out Rob Winch's talk at Spring I/O from May 2023 on Enterprise Security with Spring Authorization Server. Towards the end, his talk covers plugging in a key rotation strategy and he mentions that you can easily replace the in-memory implementation from his sample repo with one backed by a database (using Spring Data JDBC or JPA for example).
These are the tips you'll need to scale your k8s-based authz server in production.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论