使用当前 httpclient (4.x) 进行 RestTemplate 基本或摘要身份验证

发布于 2025-01-07 06:02:13 字数 879 浏览 5 评论 0原文

我正在尝试使用 RestTemplatehttpclient (4.x) 进行摘要式(或基本)身份验证。

由于我找不到任何有关如何实际执行此操作的相关示例,因此我尝试了各种方法来挂钩各种 httpclient 工件,但没有成功 - 本质上,根本没有发送 Authentication 标头。

我当前的实现是:

DefaultHttpClient newHttpClient = new DefaultHttpClient();
Credentials credentials = new UsernamePasswordCredentials( username, password );
AuthScope authScope = new AuthScope( host, port, AuthScope.ANY_REALM );
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials( authScope, credentials );
newHttpClient.setCredentialsProvider( credentialsProvider );

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory( newHttpClient );
restTemplate.setRequestFactory( requestFactory );

我做错了什么吗?任何地方都有这方面的工作示例吗? 任何帮助表示赞赏。 谢谢。

I'm trying to do Digest mostly (or Basic) Authentication using RestTemplate and httpclient (4.x).

Since I couldn't find any relevant examples of how to actually do this, I have attempted various ways to hook the various httpclient artifacts, with no luck - essentially, no Authentication header is sent at all.

My current implementation is:

DefaultHttpClient newHttpClient = new DefaultHttpClient();
Credentials credentials = new UsernamePasswordCredentials( username, password );
AuthScope authScope = new AuthScope( host, port, AuthScope.ANY_REALM );
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials( authScope, credentials );
newHttpClient.setCredentialsProvider( credentialsProvider );

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory( newHttpClient );
restTemplate.setRequestFactory( requestFactory );

Is there something I'm doing wrong? Is there also a working example for this anywhere?
Any help is appreciated.
Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自由如风 2025-01-14 06:02:13

尝试实现您自己的 RequestFactory 以实现抢占式身份验证。

public class PreEmptiveAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {

public PreEmptiveAuthHttpRequestFactory(DefaultHttpClient client) {
    super(client);
}

@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
    AuthCache authCache = new BasicAuthCache();
    BasicScheme basicAuth = new BasicScheme();
    HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort());
    authCache.put(targetHost, basicAuth);
    BasicHttpContext localcontext = new BasicHttpContext();
    localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);

    return localcontext;
}
}

然后就使用它:

HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory( newHttpClient );

希望它能帮助


如何设置用户名和密码(复制自@bifur的评论)

您可以使用UserNamePasswordCredentials

UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(getUsername(),getPassword()); 
client.getCredentialsProvider().setCredentials(new AuthScope(getHost(), getPort(), AuthScope.ANY_REALM), credentials); 

并且只需使用之前工厂中的客户端

HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory(client);

Try implementing your own RequestFactory in order to achieve preemptive authentication.

public class PreEmptiveAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {

public PreEmptiveAuthHttpRequestFactory(DefaultHttpClient client) {
    super(client);
}

@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
    AuthCache authCache = new BasicAuthCache();
    BasicScheme basicAuth = new BasicScheme();
    HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort());
    authCache.put(targetHost, basicAuth);
    BasicHttpContext localcontext = new BasicHttpContext();
    localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);

    return localcontext;
}
}

An then just use it:

HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory( newHttpClient );

Hope it helps


how to set the username and password (Copied from @bifur's comment)

You can use UserNamePasswordCredentials

UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(getUsername(),getPassword()); 
client.getCredentialsProvider().setCredentials(new AuthScope(getHost(), getPort(), AuthScope.ANY_REALM), credentials); 

And just use the client in the previous factory

HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory(client);
七秒鱼° 2025-01-14 06:02:13

借助最新版本的 Spring 和 HttpClient,可以轻松执行基本身份验证和摘要身份验证。

注意:我使用的是 Spring Boot 2.xx (Spring Framework 5.xx) 和 HttpClient 4.5.x


配置 RestTemplate

我们可以配置 RestTemplate 来执行以下任一操作 抢占式非抢占式(默认)基本或摘要式身份验证。

非抢占式基本或摘要式身份验证设置

RestTemplate 使用非抢占式(即最初执行质询请求)基本或摘要式身份验证的设置是相同的。只需通过 HttpClient 库的 CredentialsProvider 类提供用户名和密码即可。

HttpClient 将根据初始请求(质询请求)的 401 响应标头自动检测服务器正在使用哪种身份验证类型,因此无需进行任何特定于身份验证类型的配置。

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    Credentials credentials = new UsernamePasswordCredentials(username, password);
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(AuthScope.ANY, credentials);

    HttpClient httpClient = HttpClients
            .custom()
            .setDefaultCredentialsProvider(credentialsProvider)
            .build();

    return builder
            .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
            .build();
}

抢占式基本身份验证设置

通过抢占式基本身份验证,它变得更加容易,Spring 开箱即用地支持它。由于只需要用户名和密码,因此建议使用抢占式基本身份验证来消除执行质询请求的额外成本。

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
            .basicAuthorization(username, password)
            .build();
}

抢占式摘要身份验证设置

Spring 不支持开箱即用的 RestTemplate 抢占式摘要身份验证。由于摘要身份验证除了用户名和密码之外还需要随机数和可能的其他服务器生成的数据(例如,不透明),以便进行自身身份验证,因此必须至少发出一个质询请求。

鉴于此,尽管直接使用 HttpClient 的库,仍然可以使用抢占式摘要身份验证。如果您仍然喜欢将摘要抢占式身份验证与 RestTemplate 结合使用,请注意,在连接到非 Spring 摘要保护的应用程序时可能会遇到一些问题。

请参阅之前的答案以使用抢占式摘要身份验证。就我个人而言,考虑到可能遇到的复杂性和问题,我不建议将抢占式摘要身份验证与 RestTemplate 一起使用。使用抢占式摘要身份验证的主要动机是出于性能目的,因此除非您对每个请求进行大量调用,否则非抢占式摘要身份验证可能是更好的选择。


使用 RestTemplate 发送请求

使用 RestTemplate 发送请求时,您不需要任何特定于身份验证类型的处理。无论您使用抢占式身份验证还是非抢占式身份验证,发送请求的代码都是相同的。

示例 GET 请求

String response = restTemplate.getForObject(url, String.class);
JSONObject result = new JSONObject(response);

示例 POST 请求

JSONObject body = new JSONObject();
// populate body

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<String> request = new HttpEntity<>(body, headers);
String response = restTemplate.postForObject(url, request, String.class);
JSONObject result = new JSONObject(response);

With the latest version of Spring and HttpClient they made it really easy to do both basic and digest authentication.

Note: I'm using Spring Boot 2.x.x (Spring Framework 5.x.x) and HttpClient 4.5.x


Configure RestTemplate

We can configure the RestTemplate to do either preemptive or non-preemptive (default) basic or digest authentication.

Non-Preemptive Basic or Digest Auth Setup

The setup for the RestTemplate to use non-preemptive (i.e. initially doing a challenge request) basic or digest authentication is the same. Just provide the username and password through the HttpClient library's CredentialsProvider class.

HttpClient will automatically detect which authentication type the server is using based on the 401 response header of the initial request (challenge request) thus no need to do any authentication type specific configuration.

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    Credentials credentials = new UsernamePasswordCredentials(username, password);
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(AuthScope.ANY, credentials);

    HttpClient httpClient = HttpClients
            .custom()
            .setDefaultCredentialsProvider(credentialsProvider)
            .build();

    return builder
            .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
            .build();
}

Preemptive Basic Auth Setup

With preemptive basic authentication its even easier, Spring supports it out of the box. Since only the username and password are needed it is advisable to use preemptive basic auth to remove the extra cost of doing the challenge request.

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
            .basicAuthorization(username, password)
            .build();
}

Preemptive Digest Auth Setup

Spring doesn't support preemptive digest auth for RestTemplate out of the box. As digest auth requires the nonce and possibly other server generated data (e.g. opaque) aside from the username and password for it to authenticate itself so at least one challenge request must be made.

Given this it is still possible to use preemptive digest auth albeit using HttpClient's library directly. If you still like to use digest preemptive auth with RestTemplate note that some issues might be encountered when connecting to non Spring digest protected applications.

Refer to previous answers to use preemptive digest auth. Personally I don't suggest using preemptive digest auth with RestTemplate given the complexity and issues that can be encountered. The primary motivation of using preemptive digest auth is for performance purposes so unless you're doing numerous calls per request non-preemptive digest auth might be a better option.


Send request using RestTemplate

You don't need any authentication type specific handling when using RestTemplate to send request. It doesn't matter if you're using preemptive or non-preemptive authentication, the code for sending requests will just be the same.

Sample GET Request

String response = restTemplate.getForObject(url, String.class);
JSONObject result = new JSONObject(response);

Sample POST Request

JSONObject body = new JSONObject();
// populate body

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<String> request = new HttpEntity<>(body, headers);
String response = restTemplate.postForObject(url, request, String.class);
JSONObject result = new JSONObject(response);
开始看清了 2025-01-14 06:02:13

我稍微修改了原来的答案:

  • 添加了 https 支持
  • 更新了废弃的内容

    公共类 PreEmptiveAuthHttpRequestFactory 扩展 HttpComponentsClientHttpRequestFactory {
    
        公共 PreEmptiveAuthHttpRequestFactory(HttpClient 客户端){
            超级(客户端);
        }
    
        @覆盖
        受保护的 HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
            AuthCache authCache = new BasicAuthCache();
            BasicScheme basicAuth = new BasicScheme();
            HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
            authCache.put(targetHost, basicAuth);
            HttpClientContext localContext = HttpClientContext.create();
            localContext.setAuthCache(authCache);
            返回本地上下文;
        }
    }
    

I modified original answer a bit:

  • added https support
  • updated depracated stuff

    public class PreEmptiveAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
    
        public PreEmptiveAuthHttpRequestFactory(HttpClient client) {
            super(client);
        }
    
        @Override
        protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
            AuthCache authCache = new BasicAuthCache();
            BasicScheme basicAuth = new BasicScheme();
            HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
            authCache.put(targetHost, basicAuth);
            HttpClientContext localContext = HttpClientContext.create();
            localContext.setAuthCache(authCache);
            return localContext;
        }
    }
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文