Spring Cloud Security 403 Forbidden错误排查与修复求助
从management service通过自身端口访问advertisement service的以下两个URL时,均返回403 Forbidden错误:
http://localhost:9002/api/v1/admin_role/alladvertisementshttp://localhost:9002/api/v1/user_role/alladvertisements
已分别为admin和user使用Bearer类型的access token,权限规则为admin处理admin_role路径请求、user处理user_role路径请求。
错误返回示例:
{ "timestamp": "2022-08-31T23:58:15.250+00:00", "status": 403, "error": "Forbidden", "path": "/api/v1/admin_role/alladvertisements" }
{ "timestamp": "2022-08-31T23:58:15.250+00:00", "status": 403, "error": "Forbidden", "path": "/api/v1/user_role/alladvertisements" }
management service的Web Security配置代码:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true) public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.csrf().disable(); http.authorizeRequests() .antMatchers("/api/v1/admin_role/*").hasAnyRole("ROLE_ADMIN") .antMatchers("/api/v1/user_role/*").hasAnyRole("ROLE_USER") .antMatchers("/actuator/health").hasAnyRole("ROLE_ADMIN") .antMatchers("/actuator/circuitbreakerevents").hasAnyRole("ROLE_ADMIN") .anyRequest() .permitAll(); } @Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider provider = keycloakAuthenticationProvider(); provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(provider); } @Override @Bean protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } }
1. 修正角色前缀匹配问题
SimpleAuthorityMapper默认会给角色自动添加ROLE_前缀,如果你的Keycloak token里的角色已经自带ROLE_前缀,就会出现重复(比如变成ROLE_ROLE_ADMIN),和配置里的ROLE_ADMIN不匹配,直接触发403。
修改configureGlobal方法,关闭自动前缀添加:
@Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider provider = keycloakAuthenticationProvider(); SimpleAuthorityMapper authorityMapper = new SimpleAuthorityMapper(); // 关闭自动添加ROLE_前缀 authorityMapper.setPrefix(""); provider.setGrantedAuthoritiesMapper(authorityMapper); auth.authenticationProvider(provider); }
如果Keycloak里的角色本身不带前缀,也可以把配置里的hasAnyRole("ROLE_ADMIN")改成hasAnyRole("ADMIN"),因为hasAnyRole方法会自动给参数添加ROLE_前缀。
2. 完善资源服务器配置
因为是微服务间用Bearer token调用,需要确保management service的配置文件(application.yml/application.properties)里添加了正确的Keycloak资源服务器配置:
keycloak: resource: your-client-id auth-server-url: http://your-keycloak-address/auth realm: your-realm-name bearer-only: true
bearer-only: true表示服务只接受Bearer token,不处理登录跳转,适配微服务间的调用场景。
3. 调整路径匹配规则
当前的/api/v1/admin_role/*只能匹配单层子路径,如果你需要匹配admin_role下的所有层级路径(比如后续可能有子接口),改成/api/v1/admin_role/**:
http.authorizeRequests() .antMatchers("/api/v1/admin_role/**").hasAnyRole("ROLE_ADMIN") .antMatchers("/api/v1/user_role/**").hasAnyRole("ROLE_USER") // 其余配置保持不变
4. 验证Token中的角色信息
用JWT解析工具(比如jwt.io)拆解你的Bearer token,确认realm_access.roles字段里包含对应的ADMIN或USER角色,且格式和配置里的规则匹配(比如是否带ROLE_前缀)。
5. 检查Keycloak客户端配置
确保Keycloak中对应客户端的范围设置正确,已经包含了需要的角色,且token生成时会把这些角色信息写入其中。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu

