问题描述

Spring Cloud Gateway 2020.x版本无法注入Feign服务和RestTemplate,注入要么报错,要么IDEA无法启动项目,一直转圈

问题相关代码

Feign服务代码如下:

@FeignClient(value = "oauth")
@Component
public interface TokenService {


    /**
     * 检查令牌
     *
     * @param token 令牌
     * @return {@link String}
     */
    @RequestMapping("/oauth/check_token")
    String checkToken(@RequestParam String token);
}

Gateway代码如下:

@Order(1)
@Component
public class Oauth2Filter implements GlobalFilter {

    @Autowired
    GatewayConfig gatewayConfig;   //这个是其他的bean 可以注入成功

    @Autowired
    TokenService tokenService; //这个是Feign服务的bean 无法注入,IDEA启动的时候也不报错,就是一只在启动循环当中

    @Autowired
    RestTemplate restTemplate; //同上
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
//         1 判断请求路径是否需要放行
        if (whiteList(path)) {
            chain.filter(exchange);
        }
        // 2 不是白名单的需要鉴权
        String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(token)) {
            // 存在token 校验token合法性
           String checkToken = tokenService.checkToken(token);
        } else {
            // 不存在 就报错返回啦
        }
        return chain.filter(exchange);
    }

    private boolean whiteList(String path) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        List<String> ignoreUrl = gatewayConfig.getIgnoreUrl();
        if (ignoreUrl.size() > 0) {
            return ignoreUrl.stream().anyMatch(n -> antPathMatcher.match(n, path));
        }
        return false;
    }

}

问题原因

网上有普遍说是filter是在bean创建前被创建的所无法注入,但是我这里只是特定的bean无法注入,所以我不认同这个说法。
强烈怀疑是Gateway 2020版的对Feign支持不友好导致的,然后就想到了,在2020版的Spring Cloud Gateway是移除了之前的robbion,并且在官方的issue里面找到了相关的问题:Failed to invoke Feign and RestTemplate in Spring Cloud 2020’s Gateway

处理方式(并没有处理方式)

尽管找到了原因,但是官方并没有给出一个解决方式,很难受!!!
发现ServerWebExchange可以获取到ApplicationContext ,那么就用ApplicationContext 获取bean试试!!! 发现可以获取到
但是获取到之后又出现了新的问题,

@Order(1)
@Component
public class Oauth2Filter implements GlobalFilter {

    @Autowired
    GatewayConfig gatewayConfig;

    TokenService tokenService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
       //  1 判断请求路径是否需要放行
        if (whiteList(path)) {
            chain.filter(exchange);
        }
        // 2 不是白名单的需要鉴权
        String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        try {
            if (null == tokenService) {
                ApplicationContext applicationContext = exchange.getApplicationContext();
                assert applicationContext != null;
                tokenService = applicationContext.getBean(TokenService.class);
            }
        } catch (BeansException e) {
            e.printStackTrace();
        }
        if (StringUtils.hasText(token)) {
            // 存在token 校验token合法性
            String checkToken = tokenService.checkToken(token);
        } else {
            // 不存在 就报错返回啦
        }
        return chain.filter(exchange);
    }

    private boolean whiteList(String path) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        List<String> ignoreUrl = gatewayConfig.getIgnoreUrl();
        if (ignoreUrl.size() > 0) {
            return ignoreUrl.stream().anyMatch(n -> antPathMatcher.match(n, path));
        }
        return false;
    }

}

上述完成之后,调用网关服务进行鉴权,发现第一次可以成功,后续同样的接口直接报错:

2021-06-01 16:30:28.770  WARN 26788 --- [     parallel-2] o.s.c.l.core.RoundRobinLoadBalancer      : No servers available for service: oauth
2021-06-01 16:30:28.771  WARN 26788 --- [oundedElastic-8] .s.c.o.l.FeignBlockingLoadBalancerClient : Load balancer does not contain an instance for the service oauth
2021-06-01 16:30:28.774 ERROR 26788 --- [oundedElastic-8] a.w.r.e.AbstractErrorWebExceptionHandler : [78a0055a-1]  500 Server Error for HTTP GET "/api/user/getName"

feign.FeignException$ServiceUnavailable: [503] during [GET] to [http://oauth/oauth/check_token?token=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ7XCJpZFwiOlwiMTQwMjMyMTYzNDgxMzkxMTA0XCIsXCJ1c2VybmFtZVwiOlwiZ3JpZFwiLFwibGVzc2VlSWRcIjpcIjEyOTc1NDgyMzc1NjM5ODU5MlwiLFwidW5pdElkXCI6XCIxMjQ3NTUxNTAyOTk3NDIyMDNcIixcIndlY2hhdFwiOnRydWUsXCJ1c2VyVHlwZVwiOjUsXCJhY2NvdW50Tm9uRXhwaXJlZFwiOnRydWUsXCJjcmVkZW50aWFsc05vbkV4cGlyZWRcIjp0cnVlLFwiYWNjb3VudE5vbkxvY2tlZFwiOnRydWV9IiwiZXhwIjoxNjE5MDkyNjc1LCJpYXQiOjE2MTkwNzgyNzV9.CgLRkP5dgfRrnWXqK2gbRDbUeXqq7hC9r7jl_Ge9GRI0Al1stXHTvg5emPydha4i36fM5QXoGIoJJYmUX1-wVA1] [TokenService#checkToken(String)]: [Load balancer does not contain an instance for the service oauth]
	at feign.FeignException.serverErrorStatus(FeignException.java:237) ~[feign-core-10.10.1.jar:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	|_ checkpoint ⇢ HTTP GET "/api/user/getName" [ExceptionHandlingWebHandler]

哎,头大,放弃了,本来是打算在gateway统一调用oauth2鉴权服务进行鉴权的,还是让具体的服务自己调用oauth2鉴权服务进行鉴权吧!!!

2021-09-02 处理方式

抱着不死心的态度,发现在官方的issue里面已经给出了处理方式,当时没有仔细阅读,结果走了很多弯路!!!

最终处理方式: 使用webclient 替换feign

代码如下:

//第一步 注入webclident   
    @Bean
    @LoadBalanced     // 如果不添加,无法通过服务名进行调用,只能通过ip调用
    public WebClient.Builder webBuilder(){
        return WebClient.builder();
    }

//第二步 在gateway当中注入
  @Autowired
    WebClient.Builder webBuilder;

//第三步 具体调用方式    这里的“lb”也可以换成http
   Mono<Object> toMono = webBuilder.baseUrl("lb://服务名/").build().get().uri(uriBuilder ->
                uriBuilder.path("/api/oauth/check_token").queryParam("参数名称", "参数值").build()
        ).header(HttpHeaders.AUTHORIZATION, token).exchangeToMono(clientResponse -> clientResponse.bodyToMono(Object.class));
	// 不调用subscribe或者block是不会调用服务的
        Object block = toMono.subscribe();

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐