Contents

RestTemplate的基本身份验证

1. 概述

在本教程中,我们将学习如何使用 Spring 的RestTemplate使用受基本身份验证保护的 RESTful 服务

一旦我们为模板设置了基本身份验证,每个请求都将被抢先发送,其中包含执行身份验证过程所需的**完整凭据。**凭据将根据基本身份验证方案的规范进行编码,并使用Authorization HTTP Header。一个例子看起来像这样:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

2. 设置RestTemplate

我们可以简单地通过为它声明一个 bean 来将RestTemplate引导到 Spring 上下文中;但是,使用基本身份验证设置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));
    }
}

主机端口值应该取决于环境,允许客户端灵活地定义一组值用于集成测试,另一组值用于生产使用。这些值可以由Spring 对属性文件的一流支持 来管理。

3. Authorization HTTP Header的手动管理

为基本身份验证创建授权标头对我们来说相当简单,因此我们可以使用几行代码手动完成:

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 Header的自动管理

Spring 3.0 和 3.1,以及现在的 4.x,对 Apache HTTP 库有很好的支持:

  • 在 Spring 3.0 中,CommonsClientHttpRequestFactory与现已停产的 HttpClient 3.x集成。
  • Spring 3.1通过HttpComponentsClientHttpRequestFactory引入了对当前HttpClient 4.x 的支持(在 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>5.0.6.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.5.3</version>
</dependency>

或者,如果我们手动构造 HTTP授权标头,那么我们将需要一个额外的库来支持编码:

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

我们可以在 Maven 存储库中找到spring-webmvchttpclientcommons-codec 版本。