You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security 6+Keycloak配置后指定接口返回403问题求助

问题排查:Spring Security 6 + Keycloak 角色授权失效

环境与问题概述

使用 Spring Security 6 + Spring Boot 3.0.4 + Keycloak 20.0.2,目标是通过 Keycloak 中的user角色控制/ap/v1/customer/customerinfo/**接口的访问权限,其他请求全部拒绝。但实际遇到两个问题:

  • 按配置运行时,合法角色请求返回403
  • 将.anyRequest().denyAll()改为.anyRequest().authenticated()后,目标接口可访问,但其他接口也能被无限制访问,权限控制失效

现有配置

application.properties

spring.security.oauth2.client.registration.keycloak.client-id=client-id
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8081/realms/realm_id
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8081/realms/realm_id
spring.security.oauth2.resourceserver.jwt.jwk-set-uri = http://localhost:8081/realms/proset/protocol/openid-connect/certs

Spring Security 配置类

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

   @Bean
   protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    http.cors().configurationSource(new CorsConfigurationSource() {
        @Override
        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowedHeaders(Collections.singletonList("*"));
            config.setAllowedMethods(Collections.singletonList("*"));
            config.setAllowedOriginPatterns(List.of("*"));
            config.setAllowCredentials(true);
            return config;
        }
    });
    http.authorizeHttpRequests()
        .requestMatchers(HttpMethod.GET, "/ap/v1/customer/customerinfo/**")
        .hasAnyRole("user")
        .anyRequest()
        .denyAll();
    http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
    return http.build();
}

排查与解决方法

1. 修正角色前缀不匹配问题

Spring Security 的hasAnyRole()方法会自动给角色添加ROLE_前缀,但 Keycloak 默认返回的角色不带该前缀。比如你配置的user角色,Spring 实际会匹配ROLE_user,但 Keycloak 返回的是user,导致匹配失败返回403。

解决方式二选一:

  • 在 Keycloak 中创建带ROLE_前缀的角色(如ROLE_user)
  • 配置 Spring Security 去掉角色前缀,并指定从正确的JWT字段读取角色:
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
    // 禁用默认的ROLE_前缀
    grantedAuthoritiesConverter.setAuthorityPrefix("");
    // 指定从Keycloak JWT的realm_access.roles字段读取角色
    grantedAuthoritiesConverter.setAuthoritiesClaimName("realm_access.roles");

    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
    converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
    return converter;
}

然后在 SecurityFilterChain 中关联该转换器:

http.oauth2ResourceServer(oauth2 -> oauth2
    .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
);

2. 修正JWT公钥地址配置错误

注意到jwk-set-uri中的realm是proset,但issuer-uri中的realm是realm_id,两者必须一致,否则Spring无法正确获取公钥验证JWT签名,导致认证失败进而授权失败。

修改jwk-set-uri为:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8081/realms/realm_id/protocol/openid-connect/certs

3. 确认角色存储的JWT字段

Keycloak的JWT中,realm级角色默认放在realm_access.roles数组,client级角色放在resource_access.{client-id}.roles数组。如果你的user是client角色,需要修改转换器的setAuthoritiesClaimName为对应的字段,比如resource_access.client-id.roles(替换为你的实际client-id)。

4. 检查请求匹配规则

确认requestMatchers的路径和请求方法与实际请求完全一致:

  • 检查接口路径是否有大小写、斜杠数量错误
  • 确认请求方法是否为GET,若使用其他方法(如POST),需调整规则或添加对应方法的匹配

内容的提问来源于stack exchange,提问作者Babak Mirghafari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 16:02:56