1. 概述
本文將演示如何配置 Spring RestTemplate 以消費使用 Digest 身份驗證保護的服務。
類似於 Basic 身份驗證,一旦在模板中設置 Digest 身份驗證,客户端將能夠完成必要的安全步驟並獲取用於 Authorization 標頭所需的信息。
Authorization: Digest
username="user1",
realm="Custom Realm Name",
nonce="MTM3NTYwOTA5NjU3OTo5YmIyMjgwNTFlMjdhMTA1MWM3OTMyMWYyNDY2MGFlZA==",
uri="/spring-security-rest-basic-auth/api/bars/1",
....使用這些數據,服務器可以正確地驗證請求並返回 200 OK 響應。
2. 設置 RestTemplate
需要將 RestTemplate 聲明為 Spring 上下文中一個 Bean – 這在 XML 或純 Java 中都相對簡單,只需使用 @Bean 註解:
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.core5.http.HttpHost;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import com.baeldung.client.HttpComponentsClientHttpRequestFactoryDigestAuth;
@Configuration
public class ClientConfig {
private static final String DEFAULT_USER = "user1";
private static final String DEFAULT_PASS = "user1Pass";
public ClientConfig() {
super();
}
@Bean
public RestTemplate restTemplate() {
HttpHost host = new HttpHost("http", "localhost", 8080);
CloseableHttpClient client = HttpClientBuilder.create().
setDefaultCredentialsProvider(provider()).useSystemProperties().build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactoryDigestAuth(host, client);
return new RestTemplate(requestFactory);
}
private CredentialsProvider provider() {
BasicCredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS.toCharArray());
//defining null and -1 it applies to any host and any port
final AuthScope authScope = new AuthScope(null, -1);
provider.setCredentials(authScope, credentials);
return provider;
}
}大部分 digest 訪問機制的配置都在注入到模板中的自定義 http 請求工廠 HttpComponentsClientHttpRequestFactoryDigestAuth 中進行。
請注意,我們現在已經使用具有訪問受保護 API 憑據的憑據預先配置了模板。
3. 配置摘要身份驗證
我們將利用 Spring 3.1 引入的 HttpClient 4.x 的支持——即 HttpComponentsClientHttpRequestFactory —— 通過擴展和配置它。
我們將主要配置 HttpContext 並連接我們的自定義 Digest 身份驗證邏輯:
import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
import org.apache.hc.client5.http.impl.auth.DigestScheme;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import java.net.URI;
public class HttpComponentsClientHttpRequestFactoryDigestAuth extends HttpComponentsClientHttpRequestFactory {
HttpHost host;
public HttpComponentsClientHttpRequestFactoryDigestAuth(final HttpHost host, final HttpClient httpClient) {
super(httpClient);
this.host = host;
}
//
@Override
protected HttpContext createHttpContext(final HttpMethod httpMethod, final URI uri) {
return createHttpContext();
}
private HttpContext createHttpContext() {
// Create AuthCache instance
final AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local auth cache
final DigestScheme digestAuth = new DigestScheme();
// If we already know the realm name
digestAuth.initPreemptive(new UsernamePasswordCredentials("user1", "user1Pass".toCharArray()),
"", "Custom Realm Name");
// digestAuth.overrideParamter("nonce", "MTM3NTU2OTU4MDAwNzoyYWI5YTQ5MTlhNzc5N2UxMGM5M2Y5M2ViOTc4ZmVhNg==");
authCache.put(host, digestAuth);
// Add AuthCache to the execution context
final BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
return localcontext;
}
}現在,RestTemplate 可以簡單地注入並用於測試:
@Test
public void whenSecuredRestApiIsConsumed_then200OK() throws IOException {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpGet getMethod = new HttpGet("http://localhost:8082/spring-security-rest-basic-auth/api/bars/1");
HttpResponse response = httpClient.execute(getMethod);
System.out.println("HTTP Status of response: " + response.getCode());
}為了説明完整的配置過程,本次測試也設置了用户憑據——user1 和 user1Pass。 這部分當然應該只在一次且在測試之外完成。
4. Maven 依賴
以下是 RestTemplate 和 HttpClient 庫所需的 Maven 依賴:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>5. 結論
本教程演示瞭如何設置和配置 Rest Template,以便它可以消費使用 Digest 身份驗證保護的應用程序。 REST API 本身也需要配置 Digest 安全機制。