我有一个自定义的 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 向原始服务名称发送请求。我该如何解决此问题?
如果目的是处理访问令牌超时,最好在发送请求之前使用 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());
}