【问题标题】:Apache HttpClient Digest authenticationApache HttpClient 摘要认证
【发布时间】:2011-02-26 14:53:41
【问题描述】:

基本上我需要做的是执行摘要身份验证。我尝试的第一件事是可用的官方示例here。 但是当我尝试执行它时(通过一些小的更改,使用 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());

起初我只覆盖了“realm”和“nonce”DigestScheme 参数。但事实证明,在服务器上运行的 PHP 脚本需要所有其他参数,但无论我是否指定它们,当我调用其 authenticate() 方法时,DigestScheme 都不会在 Authorization RequestPreperty 中生成它们。 PHP 脚本返回 HTTP 响应代码 200,并显示 PHP 脚本需要 cnonce、nc 和 qop 参数的消息。

我已经为此苦苦挣扎了两天,但没有运气。基于一切,我认为问题的原因是 PHP 脚本。在我看来,当应用尝试未经授权访问它时,它不会发送挑战。

有什么想法吗?

编辑: 还有一件事,我试过用 cURL 连接,它可以工作。

【问题讨论】:

  • 看起来服务器正在发送非标准挑战。你能在浏览器中尝试一下并得到一个标题跟踪吗?
  • 我创建了一个到服务器的 HttpURLConnection 并打印出响应头。下面是挑战的样子: Key = www-authenticate Value = Digest realm="REST API" qop="auth" nonce="4c063992df3dd" opaque="aba3d4b49c454e1974970e7b5514b001"

标签: java android apache-commons-httpclient digest-authentication


【解决方案1】:

这段代码 sn-p 对我有用。您必须提供可以通过查看从主机获得的 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);

【讨论】:

【解决方案2】:

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

digestAuth.processChallenge(null);

强制解释前面的输入参数。 null 参数是一个标头,基于发送的标头(如果有)。

现在使用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();
}

【讨论】:

  • 如何以编程方式确定领域?此代码适用于将在连接到许多服务器的多台计算机上运行的库。所以我不能硬编码。
  • 我如何确定“不透明”?我试图调用的服务似乎需要它,并且它会随着每个新会话而变化。
【解决方案3】:
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'

【讨论】:

    【解决方案4】:

    你们把事情弄得这么复杂。如果你阅读 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();
        }
    }
    

    【讨论】:

    • 因为 DigestScheme 需要拉出 WWW-Authenticate,所以你必须注册它,而不是返回缓存。缓存声明它是先发制人的,所有数据都已经存在了。
    猜你喜欢
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 2012-07-18
    • 2015-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多