概述

在这个教程中,我们将学习如何使用Spring的RestTemplate消费带有基本身份验证的RESTful服务

一旦我们在模板中设置了基本身份验证,每次请求都会预先包含执行身份验证过程所需的完整凭据。这些凭据会被编码,并根据基本身份验证方案的规范使用Authorization HTTP头。一个例子看起来像这样:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
进一步阅读:

TestRestTemplate介绍

This article explores the Spring Boot TestRestTemplate. It can be treated as a follow-up of The Guide to RestTemplate, which we firmly recommend to read before focusing on TestRestTemplate. TestRestTemplate can be considered as an attractive alternative of RestTemplate.

Spring Restemplate错误处理

In this short tutorial, we’ll discuss how to implement and inject the ResponseErrorHandler interface in a RestTemplate instance to gracefully handle the HTTP errors returned by remote APIs.

2. 设置RestTemplate

我们可以通过简单地在Spring上下文中声明一个bean来初始化RestTemplate;然而,为了设置带有**基本身份验证的RestTemplate**,我们需要手动干预。因此,我们不会直接声明bean,而是使用Spring的FactoryBean以增加灵活性。这个FactoryBean会在初始化时创建并配置模板:

@Component
public class RestTemplateFactory
  implements FactoryBean<RestTemplate>, InitializingBean {
 
    private RestTemplate restTemplate;

    public RestTemplate getObject() {
        return restTemplate;
    }
    public Class<RestTemplate> getObjectType() {
        return RestTemplate.class;
    }
    public boolean isSingleton() {
        return true;
    }

    public void afterPropertiesSet() {
        HttpHost host = new HttpHost("localhost", 8082, "http");
        restTemplate = new RestTemplate(
          new HttpComponentsClientHttpRequestFactoryBasicAuth(host));
    }
}

hostport值应该依赖于环境,以便客户端可以根据集成测试和生产环境定义不同的值。这些值可以通过Spring对properties文件的一流支持进行管理:使用Properties配置Spring项目

3. 手动管理Authorization HTTP头

对于基本身份验证,我们很容易创建Authorization头,所以我们可以手动编写几行代码来实现:

HttpHeaders createHeaders(String username, String password){
   return new HttpHeaders() {{
         String auth = username + ":" + password;
         byte[] encodedAuth = Base64.encodeBase64( 
            auth.getBytes(Charset.forName("US-ASCII")) );
         String authHeader = "Basic " + new String( encodedAuth );
         set( "Authorization", authHeader );
      }};
}

发送请求同样简单:

restTemplate.exchange
 (uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);

4. 自动管理Authorization HTTP头

Spring 3.0、3.1以及现在的4.x都有对Apache HTTP库的良好支持:

  • 在Spring 3.0中,CommonsClientHttpRequestFactory与现已过时的HttpClient 3.x集成。
  • Spring 3.1引入了对当前HttpClient 4.x的支持,通过HttpComponentsClientHttpRequestFactory(添加支持于JIRA SPR-6180)。
  • Spring 4.0通过HttpComponentsAsyncClientHttpRequestFactory引入了异步支持。

让我们开始使用HttpClient 4和Spring 4进行设置。

RestTemplate需要一个支持基本身份验证的HTTP请求工厂。然而,直接使用现有的HttpComponentsClientHttpRequestFactory可能会很困难,因为RestTemplate的设计没有很好地支持HttpContext,这是解决问题的关键部分。因此,我们需要子类化HttpComponentsClientHttpRequestFactory并重写createHttpContext方法:

public class HttpComponentsClientHttpRequestFactoryBasicAuth 
  extends HttpComponentsClientHttpRequestFactory {

    HttpHost host;

    public HttpComponentsClientHttpRequestFactoryBasicAuth(HttpHost host) {
        super();
        this.host = host;
    }

    protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
        return createHttpContext();
    }
    
    private HttpContext createHttpContext() {
        AuthCache authCache = new BasicAuthCache();

        BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);

        BasicHttpContext localcontext = new BasicHttpContext();
        localcontext.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
        return localcontext;
    }
}

我们在创建HttpContext时构建了基本身份验证支持。如你所见,使用HttpClient 4.x进行预授权的基本身份验证对我们来说有些麻烦。认证信息被缓存,手动设置这个认证缓存既繁琐又不直观。

现在一切就绪,RestTemplate只需添加一个BasicAuthorizationInterceptor即可支持基本身份验证方案:

restTemplate.getInterceptors().add(
  new BasicAuthorizationInterceptor("username", "password"));

然后发送请求:

restTemplate.exchange(
  "http://localhost:8082/spring-security-rest-basic-auth/api/foos/1", 
  HttpMethod.GET, null, Foo.class);

有关如何安全地保护REST服务本身的深入讨论,请参阅这篇文章

5. Maven依赖

RestTemplate本身和HttpClient库需要以下Maven依赖:

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>6.0.13</version>
</dependency>

<dependency>
   <groupId>org.apache.httpcomponents.client5</groupId>
   <artifactId>httpclient5</artifactId>
   <version>5.2.1</version>
</dependency>

如果手动构造HTTP Authorization头,我们还需要一个额外的库来支持编码支持:

<dependency>
   <groupId>commons-codec</groupId>
   <artifactId>commons-codec</artifactId>
   <version>1.10</version>
</dependency>

可以在Maven仓库找到最新版本:spring-webmvchttpclient5commons-codec

6. 结论

关于RestTemplate和安全性方面的许多信息仍然没有考虑到当前的HttpClient 4.x版本,尽管3.x分支已经过时,Spring对这个版本的支持已完全弃用。在这个文章中,我们试图通过详细讲解如何设置带有RestTemplate的基本身份验证并使用它来消费一个受保护的REST API来改变这种情况。

要深入了解本文中的代码示例之外的消费者实现和实际RESTful服务,请查看GitHub上的项目:github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-rest-basic-auth

这是一个基于Maven的项目,可以直接导入并运行。