FeignClient出现403错误,但是使用OkHttpClient或者curl请求没有问题。具体报错为「status 403 reading xxxxx#xxxx(), feign.FeignException$Forbidden: status 403 reading......」
最近在项目中使用FeignClient发https(s)请求的时候发现一个非常奇怪的问题,在上一个文章我写了将项目中的OkHttpClient替换成FeignClient,这样能使的代码好看,易于维护。但是在替换的过程中发现,有一小部分替换之后发不了请求,使用OkHttpClient时可以返回正常的数据,但是使用FeignClient时则提示403,具体的信息如下:
这个是FeignClient的日志:
这个是错误信息:
为了检查是否由于Cookie或者request header的问题导致的,使用curl命令请求,如下:
上半部分是curl命令,可以发现我只携带了一个请求头「authorization」,其他什么都没有设置了,也是可以获得正确的返回值。由于返回的信息部分属于公司财产,所以打码比较多,见谅
然后在debug的时候,发现FeignClient使用的是默认的客户端「feign.client.Default
」,我就想切换成「OkHttpClient」,按照网上的教程
- 导入依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
2. 修改配置文件
feign: httpclient: enabled: true
完成上述配置之后重新测试,还是使用的「feign.client.Default
」,默认的客户端。
之后查看FeignClientAutoConfiguration类,部分如下:
也就说在配置了{feign.okhttp.enabled=true}时,还需要判断当前容器中是否含有OkHttpClient类的对象,如果没有,则启动配置类;如果有,则配置类不生效。
所以,既然配置类不生效,那么最简单暴力的方式就是将「OkHttpFeignConfiguration」里面的信息都拷贝出来,然后自定义一个配置类。
完整的配置类代码如下:
import feign.Client; import feign.Feign; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory; import org.springframework.cloud.commons.httpclient.OkHttpClientFactory; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class FeignOkHttpConfig { @Bean @ConditionalOnMissingBean({Client.class}) public Client feignClient(okhttp3.OkHttpClient client) { return new feign.okhttp.OkHttpClient(client); } @Bean @ConditionalOnMissingBean({ConnectionPool.class}) public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) { Integer maxTotalConnections = httpClientProperties.getMaxConnections(); Long timeToLive = httpClientProperties.getTimeToLive(); TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit); } @Bean public OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) { Boolean followRedirects = httpClientProperties.isFollowRedirects(); Integer connectTimeout = httpClientProperties.getConnectionTimeout(); Boolean disableSslValidation = httpClientProperties.isDisableSslValidation(); return httpClientFactory.createBuilder(disableSslValidation) .connectTimeout((long)connectTimeout, TimeUnit.SECONDS) .followRedirects(followRedirects) .connectionPool(connectionPool) .build(); } }
重启之后再次测试,成功解决问题。