Java Apache HttpClient 摘要身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2954434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Apache HttpClient Digest authentication
提问by Milan
Basically what I need to do is to perform digest authentication. First thing I tried is the official example available here. But when I try to execute it(with some small changes, Post instead of the the Get method) I get a
基本上我需要做的是执行摘要认证。我尝试的第一件事是此处提供的官方示例。但是当我尝试执行它时(有一些小的变化,Post 而不是 Get 方法)我得到了
org.apache.http.auth.MalformedChallengeException: missing nonce in challange
at org.apache.http.impl.auth.DigestScheme.processChallenge(DigestScheme.java:132)
When this failed I tried using:
当这失败时,我尝试使用:
DefaultHttpClient client = new DefaultHttpClient();
client.getCredentialsProvider().setCredentials(new AuthScope(null, -1, null), new UsernamePasswordCredentials("<username>", "<password>"));
HttpPost post = new HttpPost(URI.create("http://<someaddress>"));
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("domain", "<username>"));
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
DigestScheme digestAuth = new DigestScheme();
digestAuth.overrideParamter("algorithm", "MD5");
digestAuth.overrideParamter("realm", "http://<someaddress>");
digestAuth.overrideParamter("nonce", Long.toString(new Random().nextLong(), 36));
digestAuth.overrideParamter("qop", "auth");
digestAuth.overrideParamter("nc", "0");
digestAuth.overrideParamter("cnonce", DigestScheme.createCnonce());
Header auth = digestAuth.authenticate(new
UsernamePasswordCredentials("<username>", "<password>"), post);
System.out.println(auth.getName());
System.out.println(auth.getValue());
post.setHeader(auth);
HttpResponse ret = client.execute(post);
ByteArrayOutputStream v2 = new ByteArrayOutputStream();
ret.getEntity().writeTo(v2);
System.out.println("----------------------------------------");
System.out.println(v2.toString());
System.out.println("----------------------------------------");
System.out.println(ret.getStatusLine().getReasonPhrase());
System.out.println(ret.getStatusLine().getStatusCode());
At first I have only overridden "realm" and "nonce" DigestScheme parameters. But it turned out that PHP script running on the server requires all other params, but no matter if I specify them or not DigestScheme doesn't generate them in the Authorization RequestPreperty when I call its authenticate() method. And PHP script returns HTTP response code 200 with a message that PHP script requires cnonce, nc and qop parameters.
起初我只覆盖了“realm”和“nonce”DigestScheme 参数。但事实证明,在服务器上运行的 PHP 脚本需要所有其他参数,但无论我是否指定它们,DigestScheme 都不会在我调用其 authenticate() 方法时在 Authorization RequestPreperty 中生成它们。并且 PHP 脚本返回 HTTP 响应代码 200,并显示 PHP 脚本需要 cnonce、nc 和 qop 参数的消息。
I've been struggling with this for two days, and no luck. Based on everything I think that the cause of the problem is the PHP script. It looks to me that it doesn't send a challenge when app tries to access it unauthorized.
我已经为此苦苦挣扎了两天,但没有运气。基于一切,我认为问题的原因是 PHP 脚本。在我看来,当应用程序尝试未经授权访问它时,它不会发送挑战。
Any ideas anyone?
任何人的想法?
Edit: One more thing, I've tried connecting with cURL and it works.
编辑:还有一件事,我尝试与 cURL 连接并且它有效。
回答by user1107423
I managed to do a Digest login using digestScheme
after verifying the code.
digestScheme
在验证代码后,我设法使用 Digest 登录。
digestAuth.processChallenge(null);
Forces the previous input parameters to be interpreted. The null parameter is a header, based on the header sent, if any.
强制解释先前的输入参数。null 参数是一个标头,基于发送的标头(如果有)。
Now qop/nc
is used and digestScheme works as required.
Running it on android
现在 qop/nc
使用并且digestScheme按需要工作。在安卓上运行
digestAuth.overrideParamter("algorithm", "MD5");
digestAuth.overrideParamter("realm", serverRealm);
digestAuth.overrideParamter("nonce", Long.toString(new Random().nextLong(), 36));
digestAuth.overrideParamter("qop", "auth");// not effective
digestAuth.overrideParamter("nc",""+sequence);//nt effective
digestAuth.overrideParamter("cnonce", DigestScheme.createCnonce());
digestAuth.overrideParamter("opaque","ba897c2f0f3de9c6f52d");
String err;
try
{
digestAuth.processChallenge(null);
//force qop in use chalange on return header ????!!!!
}
catch (Exception e)
{
err=e.getLocalizedMessage();
}
回答by Ark Xu
You guys make it so complicated. If you read the documentation of apache httpclient, it would be super easy.
你们搞得这么复杂。如果您阅读 apache httpclient 的文档,那将非常容易。
protected static void downloadDigest(URL url, FileOutputStream fos)
throws IOException {
HttpHost targetHost = new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpClientContext context = HttpClientContext.create();
String credential = url.getUserInfo();
if (credential != null) {
String user = credential.split(":")[0];
String password = credential.split(":")[1];
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(user, password));
AuthCache authCache = new BasicAuthCache();
DigestScheme digestScheme = new DigestScheme();
authCache.put(targetHost, digestScheme);
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
}
HttpGet httpget = new HttpGet(url.getPath());
CloseableHttpResponse response = httpClient.execute(targetHost, httpget, context);
try {
ReadableByteChannel rbc = Channels.newChannel(response.getEntity().getContent());
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
} finally {
response.close();
}
}
回答by Ted Yang
This code snippet worked for me. You have to provide the realm which you can get by looking at the 401 response header you get from the host.
这个代码片段对我有用。您必须提供通过查看从主机获得的 401 响应标头可以获得的领域。
val credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(user, password));
val authCache = new BasicAuthCache();
val digestScheme = new DigestScheme();
digestScheme.overrideParamter("realm", "**Name of the Realm**");
// Nonce value
digestScheme.overrideParamter("nonce", "whatever");
authCache.put(targetHost, digestScheme);
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
val httpget = new HttpGet(url);
val response = httpClient.execute(targetHost, httpget, context);
回答by Nurlan Sofiyev
private static byte[] downloadFileWithDigitAuth(String url, String username, String password) {
byte[] bytes = null;
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet(url);
HttpContext httpContext = new BasicHttpContext();
CloseableHttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpGet, httpContext);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
Header authHeader = httpResponse.getFirstHeader(AUTH.WWW_AUTH);
DigestScheme digestScheme = new DigestScheme();
/*
override values if need
No need override values such as nonce, opaque, they are generated by server side
*/
digestScheme.overrideParamter("realm", "User Login Required !!");
digestScheme.processChallenge(authHeader);
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
httpGet.addHeader(digestScheme.authenticate(creds, httpGet, httpContext));
httpResponse.close();
httpResponse = httpClient.execute(httpGet);
}
bytes = IOUtils.toByteArray(httpResponse.getEntity().getContent());
} catch (IOException | MalformedChallengeException | AuthenticationException e) {
e.printStackTrace();
}
finally {
if (httpResponse != null) {
try {
httpResponse.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bytes;
}
Gradle :
摇篮:
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.12'
compile group: 'commons-io', name: 'commons-io', version: '2.6'