Spring @LoadBalanced RestTemplate 无法解析拦截器内部

问题描述 投票:0回答:1

我有一个自定义的 RestTemplate 来注册拦截器:

  @Bean
  @LoadBalanced
  public RestTemplate restTemplate() {
    var timeout = 15000;
    var clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    clientHttpRequestFactory.setConnectionRequestTimeout(timeout);
    RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
    restTemplate.setInterceptors(Collections.singletonList(bearerTokenInterceptor));
    return restTemplate;
  }

拦截器尝试执行请求,如果响应未经授权,它将从身份代理获取新的访问令牌并重试(以处理访问令牌超时):

  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    log.info("request get uri: {}", request.getURI());
    ClientHttpResponse response = execution.execute(request, body);

    if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {
      accessToken = keycloakService.fetchAccessToken();

      HttpRequest retryRequest = new HttpRequestWrapper(request) {
        @Override
        public HttpHeaders getHeaders() {
          HttpHeaders headers = new HttpHeaders();
          headers.putAll(super.getHeaders());
          headers.setBearerAuth(accessToken);
          return headers;
        }
      };
      response = execution.execute(retryRequest, body); // Retry the request
    }
    return response;
  }

第一次执行成功,负载均衡器将服务名称解析为从服务注册表检索到的正确主机。然而,检索新的访问令牌后的第二个似乎没有通过负载均衡器,并使用端口 80 向原始服务名称发送请求。我该如何解决此问题?

spring-mvc resttemplate spring-cloud-loadbalancer
1个回答
0
投票

如果目的是处理访问令牌超时,最好在发送请求之前使用 JwtDecoder 在本地检查超时。这样,请求只发送一次,负载均衡器仍然在工作。

  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    List<String> authorization = request.getHeaders().get("Authorization");
    if (authorization == null || jwtUtils.isExpired(authorization.getFirst().substring(7))) {
      accessToken = keycloakService.fetchAccessToken();
      request.getHeaders().setBearerAuth(accessToken);

    ClientHttpResponse response = execution.execute(request, body); // Retry the request

    return response;
  }


  public boolean isExpired(String accessToken) {
    Jwt jwt = jwtDecoder.decode(accessToken);

    Instant expiration = jwt.getExpiresAt();
    return expiration.isBefore(Instant.now());
  }
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.