Apache HTTPClient DigestAuth 不转发来自 Challenge 的“不透明”值
Posted
技术标签:
【中文标题】Apache HTTPClient DigestAuth 不转发来自 Challenge 的“不透明”值【英文标题】:Apache HTTPClient DigestAuth doesn't forward "opaque" value from Challenge 【发布时间】:2018-10-28 02:55:54 【问题描述】:我正在尝试对我无法控制的第 3 方 Web 服务使用带有 HTTP 客户端的 Digest 身份验证。
我从这里开始使用示例代码:
http://hc.apache.org/httpcomponents-client-4.5.x/httpclient/examples/org/apache/http/examples/client/ClientPreemptiveDigestAuthentication.java
在尝试下面描述的下一步之前,我让它与httpbin.org
一起工作。
看来,我正在使用的目标第 3 方服务需要将 opaque
值从初始响应的 WWW-Authentication
标头复制到下一个请求的 Authorization
标头,如此处所述:
https://security.stackexchange.com/questions/24425/what-is-the-opaque-field-in-http-digest-access-authentication-used-for
但是,我打开了线路记录并逐步执行了代码(这实际上只是上面链接的示例代码,无需在此处复制/粘贴),我看到 opaque
没有被复制。
有什么想法可以防止它被复制吗?
我什至尝试覆盖 processChallenge
方法:
DigestScheme digestAuth = new DigestScheme()
@Override
public void processChallenge(
Header header) throws MalformedChallengeException
但似乎此时引入参数中的任何值在下一个请求中都会被忽略。
【问题讨论】:
【参考方案1】:最终通过显式覆盖 Authorize
标头来修复,而不是依赖 HttpClient 的内部自动完成:
package [...];
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.*;
import org.testng.Assert;
public class DigestTest
private static final String URL
= "https://...";
private static final String PASSWORD = ...;
private static final String USER = ...;
public static void main(String[] args) throws Exception
new DigestTest().run();
public void run() throws Exception
HttpGet httpget = new HttpGet(URL);
HttpHost target
= new HttpHost(httpget.getURI().getHost(), 443, "https");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials
= new UsernamePasswordCredentials(USER, PASSWORD);
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
credentials);
CookieStore cookieStore = new BasicCookieStore();
CloseableHttpClient httpclient
= HttpClients.custom().setDefaultCookieStore(cookieStore)
.setDefaultCredentialsProvider(credsProvider).build();
try
DigestScheme digestAuth = new DigestScheme();
digestAuth.overrideParamter("qop", "auth");
digestAuth.overrideParamter("nc", "0");
digestAuth.overrideParamter("cnonce", DigestScheme.createCnonce());
AuthCache authCache = new BasicAuthCache();
authCache.put(target, digestAuth);
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
CloseableHttpResponse response;
response = httpclient.execute(target, httpget, localContext);
Map<String, String> wwwAuth = Arrays
.stream(response.getHeaders("WWW-Authenticate")[0]
.getElements())
.collect(Collectors.toMap(HeaderElement::getName,
HeaderElement::getValue));
// the first call ALWAYS fails with a 401
Assert.assertEquals(response.getStatusLine().getStatusCode(), 401);
digestAuth.overrideParamter("opaque", wwwAuth.get("opaque"));
digestAuth.overrideParamter("nonce", wwwAuth.get("nonce"));
digestAuth.overrideParamter("realm", wwwAuth.get("Digest realm"));
Header authenticate = digestAuth.authenticate(credentials, httpget,
localContext);
httpget.addHeader(authenticate);
response = httpclient.execute(target, httpget, localContext);
// the 2nd call is the real deal
Assert.assertEquals(response.getStatusLine().getStatusCode(), 200);
System.out.println(IOUtils
.toString(response.getEntity().getContent(), "utf-8"));
finally
httpclient.close();
【讨论】:
以上是关于Apache HTTPClient DigestAuth 不转发来自 Challenge 的“不透明”值的主要内容,如果未能解决你的问题,请参考以下文章
commons-httpclient 和 httpclient 之间有啥关系,都来自 apache
新旧apache HttpClient 获取httpClient方法
Apache HttpClient API 中的 CloseableHttpClient 和 HttpClient 有啥区别?