微服务间使用Keycloak Token通信失败,如何解决?
问题根因
- 网关当前仅配置了
oauth2Login(),这是为浏览器端用户登录设计的认证流程,未配置资源服务器逻辑来验证Bearer Token,导致微服务携带Token调用时无法通过认证。 - 你传递的是
ID Token,ID Token主要用于用户身份认证,而非服务间资源访问,应该使用Access Token。 - 微服务调用时的
Authorization头格式可能不规范,正确格式应为Bearer <token>。
解决方案
1. 改造网关安全配置,添加资源服务器支持
修改SecurityConfig,加入oauth2ResourceServer()配置,让网关能够验证Bearer Token:
@Configuration @EnableWebFluxSecurity public class SecurityConfig { @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveClientRegistrationRepository clientRegistrationRepository) { // 配置OIDC登录(浏览器端) http.authorizeExchange() .pathMatchers("/app/").authenticated() .and().cors() .and().oauth2Login(); // 配置资源服务器,验证Bearer Token(服务间调用) http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtDecoder(jwtDecoder()))); // 配置OIDC登出 http.logout(logout -> logout.logoutSuccessHandler( new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository))); // 所有请求需要认证 http.authorizeExchange().anyExchange().authenticated(); // 允许同源iframe http.headers().frameOptions().mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN); // 禁用CSRF http.csrf().disable(); return http.build(); } // 配置JWT解码器,从Keycloak获取公钥验证Token @Bean public JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withIssuerLocation("http://localhost:8280/auth/realms/Default") .build(); } }
2. 替换Token类型:使用Access Token而非ID Token
修改网关控制器代码,获取Access Token传递给微服务:
@RestController @RequestMapping("/aiv") public class UserController { @Autowired DataSource dataSource; @PostMapping(path = "/authenticate", produces = MediaType.APPLICATION_JSON_VALUE) public String getAuth(@RequestBody Map<String, Object> data, Authentication authentication) { // 获取Access Token(而非ID Token) OAuth2AuthenticationToken oAuth2Token = (OAuth2AuthenticationToken) authentication; String accessToken = oAuth2Token.getPrincipal().getAttribute("access_token"); // 补全Bearer前缀 data.put("Authorization", "Bearer " + accessToken); String responseData = new DefaultAuthenticateImpl().authenticateByPassword(data, dataSource); return responseData; } }
3. 确保微服务调用时的Authorization头格式正确
检查微服务的调用代码,确保Authorization头符合Bearer <token>格式(网关传递时已处理,若单独传入Token需补全前缀):
public String getRequest(String stUrl, Map<String, Object> data) { try { HttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet(stUrl); // 遍历添加头信息 for (Map.Entry<String, Object> entry : data.entrySet()) { httpget.addHeader(entry.getKey(), entry.getValue().toString()); } // 若直接传入Token,需补全格式 // httpget.setHeader("Authorization", "Bearer " + <<AuthToken>>); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String resp = httpclient.execute(httpget, responseHandler); return resp; } catch (Exception e) { e.printStackTrace(); // 建议打印异常便于排查 return null; } }
4. 验证Keycloak配置
- 确保Keycloak客户端的Access Token Lifespan设置合理,避免Token提前过期。
- 客户端的
Access Type需设置为confidential(你的配置已使用client-secret,此环节无需调整)。
内容的提问来源于stack exchange,提问作者user3458271
相关产品推荐
相关产品推荐

