概述
在这个教程中,我们将学习如何使用Spring的RestTemplate
来消费带有基本身份验证的RESTful服务。
一旦我们在模板中设置了基本身份验证,每次请求都会预先包含执行身份验证过程所需的完整凭据。这些凭据会被编码,并根据基本身份验证方案的规范使用Authorization
HTTP头。一个例子看起来像这样:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
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));
}
}
host
和port
值应该依赖于环境,以便客户端可以根据集成测试和生产环境定义不同的值。这些值可以通过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-webmvc,httpclient5和commons-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的项目,可以直接导入并运行。