使用 RestTemplate 的 Spring 4.0.0 基本身份验证

我目前正在研究将第三方应用程序与我们的本地报告系统集成。我想使用基本身份验证实现REST调用,但在Spring 4.0.0中面临问题。我有一个简单的解决方案,效果很好:

final RestTemplate restTemplate = new RestTemplate();
final String plainCreds = "username:password";
final byte[] plainCredsBytes = plainCreds.getBytes();
final byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
final String base64Creds = new String(base64CredsBytes);

final HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
final HttpEntity<String> request = new HttpEntity<String>(headers);

final ResponseEntity<MyDto> response = restTemplate.exchange("myUrl", HttpMethod.GET, request, MyDto.class);
final MyDto dot = response.getBody();

但想重写它以使用以下方式使用ClientHttpRequestFactory

final RestTemplate restTemplate = new RestTemplate(createSecureTransport("username", "password"));

private ClientHttpRequestFactory createSecureTransport(final String username, final String password) {
    final HttpClient client = new HttpClient();
    final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
    client.getState().setCredentials(new AuthScope(null, 9090, AuthScope.ANY_REALM), credentials);
    return new CommonsClientHttpRequestFactory(client);
}

此代码未编译,因为 CommonsClientHttpRequestFactory 类在 Spring 4.0.0 中不再存在。有人知道任何替代解决方案吗?我在这个REST世界中是相当新的,因此任何帮助将不胜感激。


答案 1

我知道这是一个老问题,但我自己一直在寻找这个问题的答案。在配置 RestTemplate 时,您需要添加一个 RestTemplate 拦截器。以下注释配置示例:

@Bean
public RestTemplate restTemplate() {

    final RestTemplate restTemplate = new RestTemplate();

    restTemplate.setMessageConverters(Arrays.asList(
            new FormHttpMessageConverter(),
            new StringHttpMessageConverter()
    ));
    restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("client", "secret"));

    return restTemplate;
}

Javadoc for BasicAuthorizationInterceptor.

我被困在这个上面好几个小时。也许它会在不久的将来帮助某人。


答案 2

带有 HttpClient 4.3 编辑的 http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1/:

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

  1. Spring 3.0,与现已停产的 HttpClient 3.x 集成CommonsClientHttpRequestFactory
  2. Spring 3.1 引入了对当前 HttpClient 4.x 通过的支持(在 JIRA SPR-6180 中添加了支持)HttpComponentsClientHttpRequestFactory)
  3. Spring 4.0 通过HttpComponentsAsyncClientHttpRequestFactory

让我们开始使用 HttpClient 4 和 Spring 4 进行设置。

这将需要一个HTTP请求工厂 - 一个支持基本身份验证的工厂 - 到目前为止,一切都很好。然而,直接使用现有的将被证明是困难的,因为的架构设计没有很好的支持 - 一个工具性的拼图。因此,我们需要子类化并覆盖该方法:(取自GitHub上的soluvas框架)RestTemplateHttpComponentsClientHttpRequestFactoryRestTemplateHttpContextHttpComponentsClientHttpRequestFactorycreateHttpContext)

package org.soluvas.commons.util;

import java.net.URI;

import javax.annotation.Nullable;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.HttpClient;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * From http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1/
 * 
 * <p>And with that, everything is in place – the {@link RestTemplate} will now be able to support the Basic Authentication scheme; a simple usage pattern would be:
 * 
 * <pre>
 * final AuthHttpComponentsClientHttpRequestFactory requestFactory = new AuthHttpComponentsClientHttpRequestFactory(
 *                  httpClient, host, userName, password);
 * final RestTemplate restTemplate = new RestTemplate(requestFactory);
 * </pre>
 *   
 * And the request:
 *
 * <pre>
 * restTemplate.get("http://localhost:8080/spring-security-rest-template/api/foos/1", Foo.class);
 * </pre>
 * 
 * @author anton
 */
public class AuthHttpComponentsClientHttpRequestFactory extends
        HttpComponentsClientHttpRequestFactory {

    protected HttpHost host;
    @Nullable
    protected String userName;
    @Nullable
    protected String password;

    public AuthHttpComponentsClientHttpRequestFactory(HttpHost host) {
        this(host, null, null);
    }

    public AuthHttpComponentsClientHttpRequestFactory(HttpHost host, @Nullable String userName, @Nullable String password) {
        super();
        this.host = host;
        this.userName = userName;
        this.password = password;
    }

    public AuthHttpComponentsClientHttpRequestFactory(HttpClient httpClient, HttpHost host) {
        this(httpClient, host, null, null);
    }

    public AuthHttpComponentsClientHttpRequestFactory(HttpClient httpClient, HttpHost host, 
            @Nullable String userName, @Nullable String password) {
        super(httpClient);
        this.host = host;
        this.userName = userName;
        this.password = password;
    }

    @Override
    protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
       // Create AuthCache instance
        AuthCache authCache = new BasicAuthCache();
        // Generate BASIC scheme object and add it to the local auth cache
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);

        // Add AuthCache to the execution context
        HttpClientContext localcontext = HttpClientContext.create();
        localcontext.setAuthCache(authCache);

        if (userName != null) {
            BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(new AuthScope(host), new UsernamePasswordCredentials(userName, password));
            localcontext.setCredentialsProvider(credsProvider);
        }
        return localcontext;        
    }

}

正是在这里 - 在创建 - 内置了基本的身份验证支持。如您所见,使用 HttpClient 4.x 执行抢占式基本身份验证有点负担:身份验证信息被缓存,设置此身份验证缓存的过程非常手动且不直观HttpContext

这样,一切都已准备就绪 - 现在将能够支持基本身份验证方案;一个简单的使用模式是:RestTemplate

final AuthHttpComponentsClientHttpRequestFactory requestFactory =
    new AuthHttpComponentsClientHttpRequestFactory(
                httpClient, host, userName, password);
final RestTemplate restTemplate = new RestTemplate(requestFactory);

和请求:

restTemplate.get(
    "http://localhost:8080/spring-security-rest-template/api/foos/1",
    Foo.class);

有关如何保护 REST 服务本身的深入讨论,请查看本文


推荐