Java ApacheHttpClient摘要身份验证

Java ApacheHttpClient摘要身份验证,java,android,apache-commons-httpclient,digest-authentication,Java,Android,Apache Commons Httpclient,Digest Authentication,基本上我需要做的是执行摘要身份验证。我尝试的第一件事是提供的官方示例。 但当我尝试执行它时(通过一些小的更改,Post而不是Get方法),我得到了一个 失败后,我尝试使用: DefaultHttpClient client = new DefaultHttpClient(); client.getCredentialsProvider().setCredentials(new AuthScope(null, -1, null), new UsernamePasswordCredentials("

基本上我需要做的是执行摘要身份验证。我尝试的第一件事是提供的官方示例。 但当我尝试执行它时(通过一些小的更改,Post而不是Get方法),我得到了一个

失败后,我尝试使用:

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());
DefaultHttpClient=newdefaulthttpclient();
client.getCredentialsProvider().setCredentials(新的AuthScope(null,-1,null),新的UsernamePasswordCredentials(“,”);
HttpPost=newhttppost(URI.create(“http://”);
List nvps=new ArrayList();
添加(新的BasicNameValuePair(“域”,“域”);
setEntity(新的UrlEncodedFormEntity(nvps,HTTP.UTF_8));
DigestScheme digestAuth=新的DigestScheme();
digestAuth.OverrideParameter(“算法”、“MD5”);
digestAuth.OverrideParameter(“领域”、“http://”);
digestAuth.OverrideParameter(“nonce”,Long.toString(new Random().nextLong(),36));
digestAuth.OverrideParameter(“qop”、“auth”);
digestAuth.OverrideParameter(“nc”、“0”);
digestAuth.OverrideParameter(“cnonce”,DigestScheme.CreateConce());
Header auth=digestAuth.authenticate(新)
用户名密码凭据(“,”),post);
System.out.println(auth.getName());
System.out.println(auth.getValue());
post.setHeader(auth);
HttpResponse ret=client.execute(post);
ByteArrayOutputStream v2=新建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());
首先,我只覆盖了“realm”和“nonce”DigestScheme参数。但事实证明,在服务器上运行的PHP脚本需要所有其他参数,但无论我是否指定它们,DigestScheme在调用其authenticate()方法时都不会在Authorization RequestPreperty中生成它们。PHP脚本返回HTTP响应代码200,并显示一条消息,表明PHP脚本需要cnonce、nc和qop参数

我已经为此挣扎了两天,运气不好。从所有方面来看,我认为问题的原因是PHP脚本。在我看来,当应用程序试图未经授权访问它时,它不会发送质询

有什么想法吗

编辑:
还有一件事,我尝试了与cURL的连接,它很有效。

在验证代码后,我设法使用
digestScheme
进行了摘要登录

digestAuth.processChallenge(null);
强制解释以前的输入参数。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();
}

你们把事情弄得这么复杂。如果您阅读ApacheHttpClient的文档,这将非常简单

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();
    }
}

这个代码片段对我很有用。您必须提供通过查看从主机获得的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);
格拉德尔:

compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.12'
compile group: 'commons-io', name: 'commons-io', version: '2.6'

看起来服务器正在发送非标准挑战。您能在浏览器中尝试一下并获得标题跟踪吗?我已经与服务器建立了一个httpurl连接,并打印出响应标题。下面是一个挑战的样子:Key=www authenticate Value=Digest realm=“REST API”qop=“auth”nonce=“4c063992df3dd”opaque=“aba3d4b49c454e1974970e7b5514b001”我如何通过编程确定领域?此代码适用于将在连接到多台服务器的多台计算机上运行的库。所以我不能硬编码。我如何确定“不透明”呢?我试图调用的服务似乎需要这样做,并且它会随着每个新会话而变化。这个答案非常有用,但它实际上是伪代码。下面是一个实际的工作示例,该示例从服务器获取
领域
nonce
不透明
值:因为DigestScheme需要获取WWW身份验证,所以您必须注册它,而不是返回缓存。缓存声明它是先发制人的,所有数据都已经存在。
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;
}
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.12'
compile group: 'commons-io', name: 'commons-io', version: '2.6'