SpringBoot3中如何配置Keycloak的client_credentials授权模式?
SpringBoot3 OAuth2资源服务器适配Keycloak Client Credentials模式及资源角色映射
问题概述
采用client_credentials授权模式,外部服务携带对应Keycloak客户端角色的Bearer Token访问应用。SpringBoot2.x使用Keycloak适配器时,可通过use-resource-role-mappings: 'true'启用客户端资源角色映射,但SpringBoot3弃用该适配器后,自行编写的OAuth2配置始终返回403,核心问题在于未正确配置资源服务器的JWT验证逻辑,以及未提取Keycloak Token中的客户端资源角色。
原SpringBoot2.x配置参考
Java配置
@Configuration @EnableWebSecurity public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.csrf().disable().authorizeRequests().antMatchers("/*").hasRole("some-role").anyRequest() .permitAll(); } }
application.yml配置
keycloak: auth-server-url: http://test-keycloak/auth realm: MyRealm resource: MyService use-resource-role-mappings: 'true'
SpringBoot3正确配置方案
核心问题解析
- 原配置未启用OAuth2资源服务器逻辑:SpringBoot3中需显式配置
oauth2ResourceServer()告知Spring Security处理Bearer Token验证。 - 默认JWT转换器不提取客户端资源角色:Keycloak在client_credentials模式下,角色存储在Token的
resource_access.{client-id}.roles字段中,需自定义转换器提取该字段。
Java配置
@Configuration @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig { @Value("${spring.security.oauth2.client.registration.keycloak.client-id}") private String clientId; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() // 配置权限规则 .authorizeHttpRequests(request -> request .requestMatchers("/*").hasRole("some-role") .anyRequest().permitAll()) // 启用OAuth2资源服务器,配置JWT验证 .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))); return http.build(); } // 自定义JWT转换器,适配Keycloak客户端角色 private JwtAuthenticationConverter jwtAuthenticationConverter() { JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); // 设置自定义的角色提取器 converter.setJwtGrantedAuthoritiesConverter(new KeycloakClientRoleExtractor(clientId)); // 使用SimpleAuthorityMapper统一角色前缀(适配hasRole方法) converter.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); return converter; } // 自定义角色提取器,从resource_access中获取客户端角色 static class KeycloakClientRoleExtractor implements Converter<Jwt, Collection<GrantedAuthority>> { private final String clientId; public KeycloakClientRoleExtractor(String clientId) { this.clientId = clientId; } @Override public Collection<GrantedAuthority> convert(Jwt jwt) { // 从JWT中获取resource_access字段 Map<String, Object> resourceAccess = jwt.getClaim("resource_access"); if (resourceAccess == null) { return Collections.emptyList(); } // 获取当前客户端的角色集合 Map<String, Object> clientRoleMap = (Map<String, Object>) resourceAccess.get(clientId); if (clientRoleMap == null) { return Collections.emptyList(); } List<String> roles = (List<String>) clientRoleMap.get("roles"); if (roles == null) { return Collections.emptyList(); } // 将角色转换为Spring Security的GrantedAuthority,添加ROLE_前缀适配hasRole return roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); } } }
application.yml配置
spring: security: oauth2: # 资源服务器配置:验证JWT的合法性 resource-server: jwt: issuer-uri: http://test-keycloak/auth/realms/MyRealm # 客户端配置:若应用需要主动获取Token则配置,仅验证外部Token时可省略,但client-id需一致 client: registration: keycloak: client-id: MyService client-secret: your-client-secret # client_credentials模式需配置客户端密钥 authorization-grant-type: client_credentials provider: keycloak: issuer-uri: http://test-keycloak/auth/realms/MyRealm
关键配置说明
- 启用资源服务器:通过
.oauth2ResourceServer()配置,让Spring Security识别并处理Bearer Token,自动完成JWT签名验证、过期检查等。 - 自定义角色提取:
KeycloakClientRoleExtractor专门提取Keycloak Token中resource_access.{client-id}.roles下的客户端角色,替代原适配器use-resource-role-mappings的功能。 - 角色前缀处理:
SimpleAuthorityMapper自动处理角色前缀,确保与hasRole()方法匹配(hasRole("some-role")对应ROLE_some-role权限)。 - 客户端密钥:client_credentials模式下,Keycloak要求客户端验证身份,需配置正确的客户端密钥。
验证要点
- 检查Token内容:确保Bearer Token中包含
resource_access.MyService.roles字段,且包含some-role角色。 - 权限方法匹配:若无需
ROLE_前缀,可修改转换器逻辑,同时将hasRole()改为hasAuthority()。
内容的提问来源于stack exchange,提问作者beatrice
相关产品推荐
相关产品推荐

