如何在RequestInterceptor中区分多端点设置请求头,解决Feign调用Bearer token为空问题
问题排查与修复方案
核心错误点
- Feign接口方法定义错误:
/oauth/token对应的接口方法被定义为static,且直接返回空的BasicAuthResponse实例。Feign只会代理接口的非静态抽象方法,静态方法不会触发远程调用,导致永远拿不到真实接口返回的token。
- Feign接口方法定义错误:
- 请求拦截器头覆盖问题:拦截器中先判断如果是
/oauth/token请求设置Basic Auth头,但是后续无判断直接覆盖Authorization头为Bearer格式,导致/oauth/token请求实际携带的是无效的Bearer头,无法通过Basic Auth校验。
- 请求拦截器头覆盖问题:拦截器中先判断如果是
- Basic Auth格式错误:
- 拼接凭证时错误使用用户名+用户名,正确应该是用户名+密码
- 生成的Basic Auth头缺少
Basic前缀,服务端无法识别这是Basic Auth凭证
- Token获取逻辑未走Feign代理:
SomeAuthTokenSupplier中调用getBasicAuthToken()调用的是静态方法,没有走Feign客户端的代理逻辑,不会发起真实的远程请求。
- Token获取逻辑未走Feign代理:
修复步骤
第一步:修正Feign接口定义
把/oauth/token的方法改成非静态抽象方法,删除写死的返回值:
@PostMapping("/oauth/token") BasicAuthResponse getBasicAuthToken();
第二步:修正拦截器的头设置逻辑
调整顺序,只有非/oauth/token的请求才设置Bearer头,避免覆盖,同时修正Basic Auth的格式:
@Bean public RequestInterceptor someAuthInterceptor() { return template -> { if(template.url().equals("/oauth/token")) { // 修正凭证拼接规则为用户名:密码,增加标准Basic前缀 String credential = properties.getCredentials().getUser() + ":" + properties.getCredentials().getPassword(); String authToken = "Basic " + Base64Utils.encodeToString(credential.getBytes(StandardCharsets.UTF_8)); template.header("Authorization", authToken); } else { // 仅业务接口添加Bearer头 template.header("Authorization", String.format("Bearer %s", tokenSupplier.getToken())); } }; }
第三步:修正Token获取逻辑,走Feign代理发起请求
注入ApplicationContext懒加载Feign客户端实例,避免循环依赖,发起真实的token获取请求:
@AllArgsConstructor class SomeClientConfig extends BaseClientConfig { private final SomeProperties properties; // 新增注入Spring上下文 private final ApplicationContext applicationContext; private final SomeAuthTokenSupplier tokenSupplier = new SomeAuthTokenSupplier(); // 原有apacheClient、retryer等Bean逻辑保持不变 private class SomeAuthTokenSupplier { private volatile String token; private volatile long retrievedOn = -1L; // 懒加载获取Feign代理实例,避免循环依赖 private someClient getClient() { return applicationContext.getBean(someClient.class); } String getToken() { if (updateTokenRequired()) { synchronized (this) { if (updateTokenRequired()) { // 走Feign代理发起真实的远程请求获取token BasicAuthResponse tokenResponse = getClient().getBasicAuthToken(); token = tokenResponse.getAccess_token(); retrievedOn = Instant.now().toEpochMilli(); } } } return token; } // 原有updateTokenRequired逻辑保持不变 } }
可选优化
- 建议按照Java命名规范将Feign接口名
someClient调整为大驼峰格式SomeClient,避免语法规范警告 - 可以直接使用
/oauth/token接口返回的expires_in字段判断token有效期,比硬编码8小时更符合对接接口的规则
内容的提问来源于stack exchange,提问作者Sophie
相关产品推荐
相关产品推荐

