集成Keycloak与Spring的WSO2 API调用权限验证失败求助
实现WSO2 APIM + Keycloak + Spring Boot的双重权限验证
要同时验证应用令牌(WSO2订阅令牌)和用户权限(Keycloak用户令牌),核心是让WSO2负责应用级的订阅校验,并将用户令牌传递给后端Spring Boot服务,由Spring Security完成用户角色的校验。以下是具体实现步骤:
一、WSO2 APIM端配置:转发用户令牌并校验应用权限
配置API转发用户令牌
- 登录WSO2 APIM Publisher,打开目标API,进入「Manage」→「Runtime Configurations」
- 在「Endpoint Configurations」的「Headers」区域,添加自定义请求头(比如
X-User-JWT),值设置为前端传入的用户令牌变量(建议前端用自定义头传递用户令牌,避免和WSO2的应用令牌冲突) - 保存API配置并重新发布
确保Keycloak密钥管理器的令牌校验能力
- 在WSO2的「Key Managers」页面,打开已配置的Keycloak密钥管理器
- 勾选「Enable OAuth2 Introspection」,配置Keycloak的令牌 introspection 端点(格式:
https://<Keycloak域名>/realms/<你的Realm>/protocol/openid-connect/token/introspect) - 填入Keycloak中创建的用于WSO2校验的客户端ID和密钥,保存配置
二、Spring Boot端配置:双重令牌校验
需要配置两个SecurityFilterChain,分别处理WSO2应用令牌的有效性校验,以及Keycloak用户令牌的角色权限校验。
1. 依赖配置
确保pom.xml中包含Spring Security OAuth2 Resource Server依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
2. Security配置类
@Configuration @EnableWebSecurity @EnableMethodSecurity public class DualAuthSecurityConfig { // 校验WSO2应用令牌:确保请求来自合法订阅的应用 @Bean public SecurityFilterChain appTokenFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/api/**") .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .issuerUri("https://<WSO2域名>/oauth2/token") .jwtAuthenticationConverter(wso2JwtConverter()) ) ); return http.build(); } // 校验Keycloak用户令牌:验证用户角色权限 @Bean public SecurityFilterChain userTokenFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/api/**") .authorizeHttpRequests(auth -> auth // 替换为你的API所需角色 .requestMatchers("/api/admin/**").hasRole("ADMIN") .requestMatchers("/api/user/**").hasRole("USER") .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .issuerUri("https://<Keycloak域名>/realms/<你的Realm>") .jwkSetUri("https://<Keycloak域名>/realms/<你的Realm>/protocol/openid-connect/certs") .jwtAuthenticationConverter(keycloakJwtConverter()) ) ); return http.build(); } // 自定义转换器:提取WSO2应用令牌中的权限(比如scope) private Converter<Jwt, AbstractAuthenticationToken> wso2JwtConverter() { return jwt -> { Collection<String> scopes = jwt.getClaimAsStringList("scope"); Collection<GrantedAuthority> authorities = scopes.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); return new JwtAuthenticationToken(jwt, authorities); }; } // 自定义转换器:处理Keycloak令牌中的角色(添加ROLE_前缀适配Spring Security) private Converter<Jwt, AbstractAuthenticationToken> keycloakJwtConverter() { return jwt -> { Map<String, Object> realmAccess = jwt.getClaim("realm_access"); Collection<String> roles = (Collection<String>) realmAccess.get("roles"); Collection<GrantedAuthority> authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); return new JwtAuthenticationToken(jwt, authorities); }; } }
3. 配置文件(application.yml)
spring: security: oauth2: resourceserver: jwt: # WSO2和Keycloak的JWT配置已在代码中通过issuerUri指定,这里可留空或统一配置
三、前端请求方式
前端发起请求时,需要在请求头中同时携带两个令牌:
Authorization: Bearer <WSO2生成的应用订阅令牌>:用于WSO2校验应用订阅权限X-User-JWT: Bearer <Keycloak颁发的用户身份令牌>:用于后端Spring Security校验用户角色
四、常见问题排查
- 应用令牌校验失败:检查WSO2中应用是否订阅了目标API,应用令牌的
scope是否包含API的权限范围 - 用户角色校验失败:检查Keycloak中用户是否被分配了对应角色,Spring Security的角色前缀是否和Keycloak的角色匹配(比如Keycloak角色是
admin,Spring Security需要ROLE_admin) - 令牌传递失败:检查WSO2的API头转发配置是否正确,后端是否能接收到
X-User-JWT头
内容的提问来源于stack exchange,提问作者d81n01
相关产品推荐
相关产品推荐

