使用抢占式基本身份验证与 RestTemplate 和 HttpClient
抢先式基本身份验证是在服务器回复 401
响应请求之前发送 http 基本身份验证凭据(用户名和密码) 的做法。这可以在使用已知需要基本身份验证的 REST api 时保存请求往返。
如 Spring 文档中所述, Apache HttpClient可以用作底层实现,通过使用 HttpComponentsClientHttpRequestFactory
来创建 HTTP 请求。HttpClient 可以配置为执行抢占式基本身份验证 。
以下类扩展 HttpComponentsClientHttpRequestFactory
以提供抢先式基本身份验证。
/**
* {@link HttpComponentsClientHttpRequestFactory} with preemptive basic
* authentication to avoid the unnecessary first 401 response asking for
* credentials.
* <p>
* Only preemptively sends the given credentials to the given host and
* optionally to its subdomains. Matching subdomains can be useful for APIs
* using multiple subdomains which are not always known in advance.
* <p>
* Other configurations of the {@link HttpClient} are not modified (e.g. the
* default credentials provider).
*/
public class PreAuthHttpComponentsClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
private String hostName;
private boolean matchSubDomains;
private Credentials credentials;
/**
* @param httpClient
* client
* @param hostName
* host name
* @param matchSubDomains
* whether to match the host's subdomains
* @param userName
* basic authentication user name
* @param password
* basic authentication password
*/
public PreAuthHttpComponentsClientHttpRequestFactory(HttpClient httpClient, String hostName,
boolean matchSubDomains, String userName, String password) {
super(httpClient);
this.hostName = hostName;
this.matchSubDomains = matchSubDomains;
credentials = new UsernamePasswordCredentials(userName, password);
}
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(new PreAuthCredentialsProvider());
context.setAuthCache(new PreAuthAuthCache());
return context;
}
/**
* @param host
* host name
* @return whether the configured credentials should be used for the given
* host
*/
protected boolean hostNameMatches(String host) {
return host.equals(hostName) || (matchSubDomains && host.endsWith("." + hostName));
}
private class PreAuthCredentialsProvider extends BasicCredentialsProvider {
@Override
public Credentials getCredentials(AuthScope authscope) {
if (hostNameMatches(authscope.getHost())) {
// Simulate a basic authenticationcredentials entry in the
// credentials provider.
return credentials;
}
return super.getCredentials(authscope);
}
}
private class PreAuthAuthCache extends BasicAuthCache {
@Override
public AuthScheme get(HttpHost host) {
if (hostNameMatches(host.getHostName())) {
// Simulate a cache entry for this host. This instructs
// HttpClient to use basic authentication for this host.
return new BasicScheme();
}
return super.get(host);
}
}
}
这可以使用如下:
HttpClientBuilder builder = HttpClientBuilder.create();
ClientHttpRequestFactory requestFactory =
new PreAuthHttpComponentsClientHttpRequestFactory(builder.build(),
"api.some-host.com", true, "api", "my-key");
RestTemplate restTemplate = new RestTemplate(requestFactory);