spring 使用restTemplate发送带有Authentication报头的GET请求

vngu2lb8  于 2023-10-15  发布在  Spring
关注(0)|答案(7)|浏览(183)

我需要通过使用RestTemplate发送带有一些Authorization头的GET请求来从服务器中检索资源。
在浏览了docs之后,我注意到没有一个GET方法接受Header作为参数,发送Header(如accept和Authorization)的唯一方法是使用exchange方法。
因为这是一个非常基本的动作,我想知道我是否错过了什么,还有其他更简单的方法吗?

trnvg8h3

trnvg8h31#

你什么都没错过。RestTemplate#exchange(..)是设置请求头的合适方法。
Here's an example(使用POST,但只需将其更改为GET并使用所需的实体)。
Here's another example.
请注意,使用GET,请求实体不必包含任何内容(除非API期望它,但这将违反HTTP规范)。它可以是一个空字符串。

smdncfj3

smdncfj32#

您可以将postForObjectHttpEntity一起使用。它看起来像这样:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);

HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String result = restTemplate.postForObject(url, entity, String.class);

在GET请求中,您通常不会发送body(这是允许的,但它没有任何用途)。在不与RestTemplate不同的情况下添加头的方法是直接使用exchangeexecute方法。get简写不支持头修改。
乍一看,这种不对称有点奇怪,也许这将在Spring的未来版本中得到修复。

6mzjoqzu

6mzjoqzu3#

下面是一个超级简单的例子,包含基本的身份验证、头和异常处理。

private HttpHeaders createHttpHeaders(String user, String password)
{
    String notEncoded = user + ":" + password;
    String encodedAuth = "Basic " + Base64.getEncoder().encodeToString(notEncoded.getBytes());
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.add("Authorization", encodedAuth);
    return headers;
}

private void doYourThing() 
{
    String theUrl = "http://blah.blah.com:8080/rest/api/blah";
    RestTemplate restTemplate = new RestTemplate();
    try {
        HttpHeaders headers = createHttpHeaders("fred","1234");
        HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
        ResponseEntity<String> response = restTemplate.exchange(theUrl, HttpMethod.GET, entity, String.class);
        System.out.println("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
    }
    catch (Exception eek) {
        System.out.println("** Exception: "+ eek.getMessage());
    }
}
2w3kk1z5

2w3kk1z54#

这些天来,像下面这样的东西就足够了:

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
restTemplate.exchange(RequestEntity.get(new URI(url)).headers(headers).build(), returnType);
jyztefdp

jyztefdp5#

所有这些答案似乎都是不完整的和/或拼凑的。看一下RestTemplate接口,它看起来确实像是要注入一个ClientHttpRequestFactory,然后requestFactory将用于创建请求,包括头、体和请求参数的任何自定义。
你要么需要一个通用的ClientHttpRequestFactory来注入到一个共享的RestTemplate中,要么需要通过new RestTemplate(myHttpRequestFactory)获取一个新的模板示例。
不幸的是,创建这样一个工厂看起来有些不平凡,即使你只想设置一个Authorization header,考虑到这可能是一个常见的需求,这是非常令人沮丧的,但至少它允许轻松使用,例如,如果你的Authorization header可以从Spring-Security Authorization对象中包含的数据创建,然后,您可以创建一个工厂,通过执行SecurityContextHolder.getContext().getAuthorization(),然后填充头,并在适当的情况下使用null检查,来设置每个请求的传出AuthorizationHeader。现在,使用该RestTemplate进行的所有出站rest调用都将具有正确的Authorization标头。
如果没有更多地强调HttpClientFactory机制,为常见情况(如向请求添加单个头)提供简单到重载的基类,RestTemplate的大多数方便方法最终都是浪费时间,因为它们很少使用。
我想看到像这样简单的东西

@Configuration
public class MyConfig {
  @Bean
  public RestTemplate getRestTemplate() {
    return new RestTemplate(new AbstractHeaderRewritingHttpClientFactory() {
        @Override
        public HttpHeaders modifyHeaders(HttpHeaders headers) {
          headers.addHeader("Authorization", computeAuthString());
          return headers;
        }
        public String computeAuthString() {
          // do something better than this, but you get the idea
          return SecurityContextHolder.getContext().getAuthorization().getCredential();
        }
    });
  }
}

目前,可用的ClientHttpRequestFactory的接口比这更难交互。更好的方法是为现有的工厂实现提供一个抽象 Package 器,使它们看起来像一个更简单的对象,如AbstractHeaderRewritingRequestFactory,目的是只替换其中的一部分功能。现在,它们是非常通用的,即使编写这些 Package 也是一项复杂的研究。

ny6fqffe

ny6fqffe6#

一个简单的解决方案是在RestTemplate的bean配置中配置所有调用所需的静态http头:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate(@Value("${did-service.bearer-token}") String bearerToken) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add((request, body, clientHttpRequestExecution) -> {
            HttpHeaders headers = request.getHeaders();
            if (!headers.containsKey("Authorization")) {
                String token = bearerToken.toLowerCase().startsWith("bearer") ? bearerToken : "Bearer " + bearerToken;
                request.getHeaders().add("Authorization", token);
            }
            return clientHttpRequestExecution.execute(request, body);
        });
        return restTemplate;
    }
}
ymzxtsji

ymzxtsji7#

注意:从Spring 5开始,RestTemplate类处于维护模式,不推荐使用它。只要有可能,就使用WebClient
也就是说,由于某些框架的某些约束或其他原因,您可能仍然需要配置RestTemplate。
与其在每个请求中提供身份验证头,不如配置一个全局RestTemplate,以便它可以被不同的组件重用。
在下面找到一个配置文件,它使用不同的方法来提供身份验证头。

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplateWithCustomApiKey(@Value("${api.key}") String apiKey) {
        return new RestTemplateBuilder()
                .requestCustomizers(clientHttpRequest -> clientHttpRequest.getHeaders().add("X-API-Key", apiKey))
                .build();
    }

    @Bean
    public RestTemplate restTemplateWithBearerTokenSupplier(JwtTokenProvider yourJwtTokenProvider) {
        return new RestTemplateBuilder()
                .requestCustomizers(clientHttpRequest -> clientHttpRequest.getHeaders().setBearerAuth(yourJwtTokenProvider.getAccessToken()))
                .build();
    }

    @Bean
    public RestTemplate restTemplateWithBasicAuth(@Value("${auth.user}") String user, @Value("${auth.pass}") String pass) {
        return new RestTemplateBuilder()
                .requestCustomizers(clientHttpRequest -> clientHttpRequest.getHeaders().setBasicAuth(user, pass))
                .build();
    }
}

相关问题