作者: Eugen Paraschiv
译者: helloworldtang
本文将演示如何使用Spring RestTemplate
消费一个需要 Digest
身份认证的RESTful资源。
与 Basic
身份认证类似,一旦在 RestTemplate
中配置了用于 Digest
身份认证的 CredentialsProvider
,客户端就可以自动生成用于身份认证的 Authorization
HTTP头:
Authorization: Digest
username="user1",
realm="Custom Realm Name",
nonce="MTM3NTYwOTA5NjU3OTo5YmIyMjgwNTFlMjdhMTA1MWM3OTMyMWYyNDY2MGFlZA==",
uri="/spring-security-rest-digest-auth/api/foos/1",
....
只要请求携带了类似上面示例中的Authorization HTTP头,服务器就会认证通过并返回200 OK。
RestTemplate
在Spring中,不管使用XML,还是Java配置都可以很容易将 RestTemplate
加载到 SpringContext
中。可以看到,Java配置的方式只需要简单地使用 @Bean
注解便可以了:
import org.apache.http.HttpHost;
import org.baeldung.client.HttpComponentsClientHttpRequestFactoryDigestAuth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ClientConfig {
@Bean
public RestTemplate restTemplate() {
HttpHost host = new HttpHost("localhost", 8080, "http");
CloseableHttpClient client = HttpClientBuilder.create().
setDefaultCredentialsProvider(provider()).useSystemProperties().build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactoryDigestAuth(host, client);
return new RestTemplate(requestFactory);;
}
private CredentialsProvider provider() {
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials("user1", "user1Pass";
provider.setCredentials(AuthScope.ANY, credentials);
return provider;
}
}
Digest
访问机制的大部分配置是通过注入到 RestTemplate
的 ClientHttpRequestFactory
来完成的。本文中, ClientHttpRequestFactory
的具体实现类是 HttpComponentsClientHttpRequestFactoryDigestAuth
。
请注意,我们现在正在配置一个可以发起一个携带凭据HTTP请求的RestTemplate
,这个凭据就可以访问需要身份认证的API。
Digest
身份认证本文主要通过Spring 3.1中引入的特性,即扩展和配置HttpClient 4.x中的 HttpComponentsClientHttpRequestFactory
。
主要是通过自定义HttpContext
,并将Digest
身份认证相关的配置整合进去:
import java.net.URI;
import org.apache.http.HttpHost;
import org.apache.http.client.AuthCache;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
public class HttpComponentsClientHttpRequestFactoryDigestAuth
extends HttpComponentsClientHttpRequestFactory {
HttpHost host;
public HttpComponentsClientHttpRequestFactoryDigestAuth(HttpHost host) {
super(httpClient);
this.host = host;
}
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
return createHttpContext();
}
private HttpContext createHttpContext() {
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local auth cache
DigestScheme digestAuth = new DigestScheme();
// If we already know the realm name
digestAuth.overrideParamter("realm", "Custom Realm Name");
authCache.put(host, digestAuth);
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
return localcontext;
}
}
现在,可以将RestTemplate
注入到测试用例中并直接使用了:
@Test
public void whenSecuredRestApiIsConsumed_then200OK() {
String uri = "http://localhost:8080/spring-security-rest-digest-auth/api/foos/1";
ResponseEntity<Foo> entity = restTemplate.exchange(uri, HttpMethod.GET, null, Foo.class);
System.out.println(entity.getStatusCode());
}
为了说明完整的配置过程,这个测试用例还配置了用户凭据-user1和user1Pass。当然,这一部分应该只在测试用例之外进行一次。
RestTemplate
和 HttpClient
所需的Maven依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.5</version>
</dependency>
本教程展示了如何配置一个自定义的RestTemplate
,使其能够消费需要 Digest
身份认证的RESTful资源。REST API本身需要使用 Digest
身份认证 进行保护。
文中用到的代码都可以在示例GitHub项目 中找到——这是一个基于maven的项目,因此应该很容易导入和运行。