Apache HttpClient Digest аутентификация
По сути, мне нужно выполнить дайджест-аутентификацию. Первое, что я попробовал, это официальный пример, доступный здесь. Но когда я пытаюсь выполнить его (с некоторыми небольшими изменениями, Post вместо метода Get), я получаю
org.apache.http.auth.MalformedChallengeException: missing nonce in challange
at org.apache.http.impl.auth.DigestScheme.processChallenge(DigestScheme.java:132)
Когда это не удалось, я попытался с помощью:
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());
Сначала у меня есть только переопределенные параметры DigestScheme "realm" и "nonce". Но оказалось, что PHP-скрипт, выполняемый на сервере, требует всех других параметров, но независимо от того, указываю я их или нет, DigestScheme не генерирует их в Authorization RequestPreperty, когда я вызываю его метод authenticate(). А PHP-скрипт возвращает HTTP-код ответа 200 с сообщением о том, что PHP-скрипт требует параметров cnonce, nc и qop.
Я боролся с этим в течение двух дней, и не повезло. Исходя из всего, я думаю, что причиной проблемы является сценарий PHP. Мне кажется, что он не отправляет вызов, когда приложение пытается получить к нему несанкционированный доступ.
Любые идеи кто-нибудь?
Изменить: Еще одна вещь, я попытался подключиться с помощью cURL, и это работает.
3 ответа
Этот фрагмент кода работал для меня. Вы должны указать область, которую вы можете получить, взглянув на заголовок ответа 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);
Мне удалось сделать дайджест, используя логин digestScheme
после проверки кода.
digestAuth.processChallenge(null);
Принудительно интерпретирует предыдущие входные параметры. Пустой параметр - это заголовок, основанный на отправленном заголовке, если таковой имеется.
Сейчас qop/nc
используется и digestScheme работает как требуется. Запуск его на Android
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();
}
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'
Вы, ребята, делаете это так сложно. Если вы прочитаете документацию по 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();
}
}