为啥来自其他用户的 Digest 身份验证 Java HttpClient 代码对我不起作用? [复制]

Posted

技术标签:

【中文标题】为啥来自其他用户的 Digest 身份验证 Java HttpClient 代码对我不起作用? [复制]【英文标题】:Why Digest authentication Java HttpClient code from other users doesn't work for me? [duplicate]为什么来自其他用户的 Digest 身份验证 Java HttpClient 代码对我不起作用? [复制] 【发布时间】:2018-10-27 18:59:16 【问题描述】:

我需要从 Java 调用 REST 服务,并且该服务需要摘要式身份验证。使用 curl --digest -c cookie.txt ... 进行测试时调用工作正常

经过几次死胡同,我在@Ted Yang 和user1107423 对问题Apache HttpClient Digest authentication 的回答中发现了一些有希望的伪代码。所以这就是我现在想要实现的。

这是我目前的尝试:

import org.apache.commons.io.IOUtils;
import org.apache.http.*;
import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.*;
import org.apache.http.protocol.BasicHttpContext;

[...]

    private String connect(URL url, String user,
            String password) throws URISyntaxException, IOException, ClientProtocolException 
        HttpHost targetHost = new HttpHost(url.getHost(),
                url.getPort(),
                url.getProtocol());

        URI uri = url.toURI();

        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(user,
                        password));
        httpclient = HttpClientBuilder.create()
                .setDefaultCredentialsProvider(credsProvider).build();

        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(targetHost, basicAuth);

        HttpClientContext context = HttpClientContext.create();
        context.setAttribute(HttpClientContext.AUTH_CACHE, authCache);

        CookieStore cookieStore = new BasicCookieStore();
        context.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);

        @SuppressWarnings("serial")
        class AuthException extends RuntimeException 

            public final Header header;

            public AuthException(Header header) 
                this.header = header;
            

            public String get(String key) 
                return Arrays.stream(header.getElements())
                    .filter(h -> h.getName().equals(key))
                    .findFirst()
                    .map(HeaderElement::getValue)
                    .get();
            
        

        BasicResponseHandler responseHandler = new BasicResponseHandler() 

            public String handleResponse(
                    HttpResponse response) throws HttpResponseException, IOException 
                if (response.getStatusLine().getStatusCode() == 401) 
                    Header header = response.getFirstHeader("WWW-Authenticate");
                    if (header != null) 
                        throw new AuthException(header);
                    
                
                return super.handleResponse(response);
            ;
        ;
        try 
            return httpclient.execute(new HttpGet(uri), responseHandler,
                    context);
         catch (AuthException req) 
            DigestScheme digestScheme = new DigestScheme();

            String realm = req.get("Digest realm");
            digestScheme.overrideParamter("realm", realm);
            String nonce = req.get("nonce");
            digestScheme.overrideParamter("nonce", nonce);

            authCache.put(targetHost, digestScheme);

            context.setCredentialsProvider(credsProvider);
            context.setAuthCache(authCache);

            return httpclient.execute(new HttpGet(uri),
                    new BasicResponseHandler(), context);
        
    

问题是我在第二次 HTTP 调用中仍然收到 401。

可能是什么原因?

更新

我还从@user1107423 的回答中添加了digestScheme.processChallenge(req.header);,但这并没有什么不同。

这是 httpclient 线路日志的最新相关部分

2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] .i.c.DefaultHttpClientConnectionOperator : Connection established 192.168.7.26:51130<->209.80.40.145:443
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] o.a.http.impl.execchain.MainClientExec   : Executing request GET /contact/rets/login?rets-version=RETS/1.7.2 HTTP/1.1
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] o.a.http.impl.execchain.MainClientExec   : Target auth state: FAILURE
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] o.a.http.impl.execchain.MainClientExec   : Proxy auth state: UNCHALLENGED
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.headers                  : http-outgoing-1 >> GET /contact/rets/login?rets-version=RETS/1.7.2 HTTP/1.1
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.headers                  : http-outgoing-1 >> Host: xxxxx.org
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.headers                  : http-outgoing-1 >> Connection: Keep-Alive
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.headers                  : http-outgoing-1 >> User-Agent: Apache-HttpClient/4.5.6 (Java/1.8.0_60)
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.headers                  : http-outgoing-1 >> Cookie: JSESSIONID=01681A32C72B20D1BE4BCFE17F5A015D
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.headers                  : http-outgoing-1 >> Accept-Encoding: gzip,deflate
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "GET /contact/rets/login?rets-version=RETS/1.7.2 HTTP/1.1[\r][\n]"
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "Host: **xxxxxxxxxxx**[\r][\n]"
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "Connection: Keep-Alive[\r][\n]"
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "User-Agent: Apache-HttpClient/4.5.6 (Java/1.8.0_60)[\r][\n]"
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "Cookie: JSESSIONID=01681A32C72B20D1BE4BCFE17F5A015D[\r][\n]"
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "Accept-Encoding: gzip,deflate[\r][\n]"
2018-10-27 13:12:58.408 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 >> "[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "HTTP/1.1 401 Unauthorized[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "Server: Apache-Coyote/1.1[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "Set-Cookie: JSESSIONID=01681A32C72B20D1BE4BCFE17F5A015D; Path=/contact[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "MIME-Version: 1.0[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "Cache-Control: private[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "RETS-Version: RETS/1.7.2[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "WWW-Authenticate: Digest realm="xxxxxxxxxx",qop="auth",nonce="b13fbc2aa074
9a42d37610c424e5d288", opaque="027f66646cdae"[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "Content-Type: text/html;charset=utf-8[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "Content-Length: 954[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "Date: Sat, 27 Oct 2018 20:12:56 GMT[\r][\n]"
2018-10-27 13:12:58.437 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "[\r][\n]"
2018-10-27 13:12:58.438 DEBUG 23768 --- [           main] org.apache.http.wire                     : http-outgoing-1 << "<html><head><title>Apache Tomcat/6.0.35 - Error report</title><style><!--H1 fo

【问题讨论】:

【参考方案1】:

原因显然是所有预先存在的示例都不需要客户端从对第二个请求的 401 响应中复制 opaquenonce 值。

【讨论】:

以上是关于为啥来自其他用户的 Digest 身份验证 Java HttpClient 代码对我不起作用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 SQL Server 不允许我启用混合身份验证模式,以便我可以使用 sa 用户将角色分配给其他用户?

c# digest身份验证

Kong Basic Auth 使用来自其他服务的身份验证来授权路由请求

如何在 android 中使用 volley 实现 Digest 身份验证

如何让 MD5/digest 身份验证适用于不同的域? [关闭]

Android异常SASL身份验证失败使用机制DIGEST-MD5