【问题标题】:Sending GET request with Authentication headers using restTemplate使用 restTemplate 发送带有身份验证标头的 GET 请求
【发布时间】:2014-02-01 18:53:03
【问题描述】:

我需要通过使用RestTemplate 发送带有一些授权标头的 GET 请求来从我的服务器检索资源。

在查看docs 之后,我注意到没有一个 GET 方法接受标头作为参数,而发送标头(如接受和授权)的唯一方法是使用 exchange 方法。

由于这是一个非常基本的操作,我想知道我是否遗漏了什么,还有另一种更简单的方法吗?

【问题讨论】:

    标签: java spring spring-mvc resttemplate


    【解决方案1】:

    你没有错过任何东西。 RestTemplate#exchange(..) 是用于设置请求标头的适当方法。

    Here's an example(使用 POST,但只需将其更改为 GET 并使用您想要的实体)。

    Here's another example.

    请注意,使用 GET,您的请求实体不必包含任何内容(除非您的 API 需要它,但这会违反 HTTP 规范)。可以是空字符串。

    【讨论】:

      【解决方案2】:

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

      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 请求中,您通常不会发送正文(这是允许的,但它没有任何用途)。添加标头而不以不同方式连接 RestTemplate 的方法是直接使用 exchange 或 execute 方法。 get 速记不支持 header 修改。

      乍一看,不对称有点奇怪,也许这将在未来的 Spring 版本中得到修复。

      【讨论】:

      • restTemplate.postForEntity(url, entity, String.class) 也可以正常工作。
      • 问题是关于 GET 请求,但这个答案是关于 POST。这是误导。没有getForObject有这样的签名。
      【解决方案3】:

      这是一个超级简单的示例,包含基本身份验证、标头和异常处理...

      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());
          }
      }
      

      【讨论】:

      • 不知道为什么这最初被否决。如果您是第一个投反对票的人,请随时在评论中解释。
      • “基本”部分不应该编码,应该是吗?
      • createHttpHeaders 方法略有错误。字符串 notEncoded = 用户 + ":" + 密码; ... headers.add("授权", "基本" + encodedAuth);
      • 我也刚刚发现“基本”也不应该被编码。这是我自己使用的如何进行身份验证的一个很好的例子,但是纠正编码会很好。
      • 修复了上面cmets中提到的Basic编码
      【解决方案4】:

      所有这些答案似乎都不完整和/或杂乱无章。查看 RestTemplate 接口,它确实看起来像是打算将ClientHttpRequestFactory 注入其中,然后该 requestFactory 将用于创建请求,包括对标头、正文和请求参数的任何自定义。

      你要么需要一个通用的ClientHttpRequestFactory 来注入一个共享的RestTemplate,要么你需要通过new RestTemplate(myHttpRequestFactory) 获取一个新的模板实例。

      不幸的是,即使您只想设置一个 Authorization 标头,创建这样一个工厂看起来也有些不简单,考虑到这可能是一个常见的要求,这非常令人沮丧,但至少它允许在以下情况下轻松使用,例如,您的 Authorization 标头可以从 Spring-Security Authorization 对象中包含的数据创建,然后您可以创建一个工厂,通过执行 SecurityContextHolder.getContext().getAuthorization() 然后填充标头,在每个请求上设置传出 AuthorizationHeader,使用 null酌情检查。现在,使用该 RestTemplate 进行的所有出站休息调用都将具有正确的 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 的接口比这更难交互。更好的是现有工厂实现的抽象包装器,这使它们看起来像 AbstractHeaderRewritingRequestFactory 之类的更简单的对象,目的是仅替换那一项功能。目前,它们的用途非常广泛,因此即使编写这些包装器也是一项复杂的研究。

      【讨论】:

      • 虽然您的“答案”非常有趣,但它读起来更像是评论而不是实际答案。
      【解决方案5】:

      如今,以下内容就足够了:

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

      【讨论】:

        【解决方案6】:

        一个简单的解决方案是在 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;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-09-21
          • 2019-08-08
          • 2016-03-15
          • 2016-07-05
          • 2020-08-25
          • 2015-07-20
          • 2017-05-08
          • 2016-05-04
          相关资源
          最近更新 更多