Java Android中使用HttpURLConnection的摘要身份验证

Java Android中使用HttpURLConnection的摘要身份验证,java,android,httpurlconnection,digest-authentication,Java,Android,Httpurlconnection,Digest Authentication,正如问题allready所说,我正在尝试在android中进行摘要身份验证。 到目前为止,我已经使用了DefaultHttpClient及其身份验证方法(使用UsernamePasswordCredentials等),但自Android 5以来,它已被弃用,并将在Android 6中删除。 因此,我将从DefaultHttpClient切换到HttpUrlConnection 现在,我正在尝试实现摘要身份验证,其工作应该非常简单,如下所述: 但是由于某种原因,getPasswordAuthent

正如问题allready所说,我正在尝试在android中进行摘要身份验证。
到目前为止,我已经使用了
DefaultHttpClient
及其身份验证方法(使用
UsernamePasswordCredentials
等),但自Android 5以来,它已被弃用,并将在Android 6中删除。
因此,我将从
DefaultHttpClient
切换到
HttpUrlConnection

现在,我正在尝试实现摘要身份验证,其工作应该非常简单,如下所述:

但是由于某种原因,
getPasswordAuthentication
从未被调用。
在我搜索这个问题的过程中,我发现了不同的帖子,说android中的
HttpUrlConnection
不支持摘要认证,但这些帖子是2010-2012年发布的,所以我不确定这是否仍然是真的。此外,我们在桌面java应用程序中使用了带摘要身份验证的
HttpUrlConnection
,它确实可以工作

我还发现了一些帖子,谈论
OkHttp
OkHttp
似乎被Android暗中使用(更具体地说是
HttpUrlConnectionImpl
)。但是这个
HttpUrlConnectionImpl
有点奇怪,它甚至没有显示在Eclipse类型层次结构中,我无法调试它。另外,它应该是
com.squareup.okhttp.internal.huc.HttpUrlConnectionImpl
,而在android中它是
com.android.okhttp.internal.http.HttpUrlConnectionImpl

因此,我无法在android中使用此
HttpUrlConnection
进行摘要身份验证。
有谁能告诉我,没有外部库怎么做

编辑:
服务器请求摘要身份验证:

WWW-Authenticate: Digest realm="Realm Name",domain="/domain",nonce="nonce",algorithm=MD5,qop="auth"

因此,基本身份验证不应该起作用,因为服务器正在请求摘要。

您是否尝试手动设置标头,如:

String basic = "Basic " + new String(Base64.encode("username:password".getBytes(),Base64.NO_WRAP ));
connection.setRequestProperty ("Authorization", basic);
在尝试执行post请求时,还要注意Jellybeans中的一些问题和一个bug:

编辑:用于摘要身份验证

看看这里

特别是这可能会起作用:

try {   
        HttpClient client = new HttpClient(
                new MultiThreadedHttpConnectionManager());

        client.getParams().setAuthenticationPreemptive(true);
        Credentials credentials = new UsernamePasswordCredentials("username", "password");
        client.getState().setCredentials(AuthScope.ANY, credentials);
        List<String> authPrefs = new ArrayList<String>(2);
        authPrefs.add(AuthPolicy.DIGEST);
        authPrefs.add(AuthPolicy.BASIC);
        client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,
                authPrefs);
        GetMethod getMethod = new GetMethod("your_url");
        getMethod.setRequestHeader("Accept", "application/xml");
        client.executeMethod(getMethod);
        int status = getMethod.getStatusCode();
        getMethod.setDoAuthentication(true);
        System.out.println("status: " + status);
        if (status == HttpStatus.SC_OK) {
            String responseBody = getMethod.getResponseBodyAsString();
            String resp = responseBody.replaceAll("\n", " ");
            System.out.println("RESPONSE \n" + resp);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
试试{
HttpClient=新的HttpClient(
新的多线程HttpConnectionManager();
client.getParams().setAuthenticationPreemptive(true);
凭证凭证=新用户名密码凭证(“用户名”、“密码”);
client.getState().setCredentials(AuthScope.ANY,credentials);
List authPrefs=new ArrayList(2);
添加(AuthPolicy.DIGEST);
添加(AuthPolicy.BASIC);
client.getParams().setParameter(AuthPolicy.AUTH\u SCHEME\u优先级,
authPrefs);
GetMethod GetMethod=新的GetMethod(“您的url”);
setRequestHeader(“接受”、“应用程序/xml”);
client.executeMethod(getMethod);
int status=getMethod.getStatusCode();
getMethod.setDoAuthentication(true);
System.out.println(“状态:+状态”);
if(status==HttpStatus.SC\u OK){
字符串responseBody=getMethod.getResponseBodyAsString();
String resp=responseBody.replaceAll(“\n”和“);
System.out.println(“响应\n”+resp);
}
}捕获(例外e){
e、 printStackTrace();
}

答案是,
HttpUrlConnection
不支持摘要

因此,您必须自己实施

您可以将以下代码用作基线实现:

这些步骤包括(参考请参见):

  • 如果得到401响应,请迭代所有
    WWW-Authenticate
    头并解析它们:
    • 检查算法是否为MD5或未定义(可选选择
      auth
      qop选项),否则忽略质询并转到下一个标题
    • 使用
      验证器获取凭据。requestPasswordAuthentication
    • 使用用户名、域和密码计算H(A1)
    • 存储规范的根URL、领域、HA1、用户名、nonce(+可选算法、不透明和客户端选择的qop选项,如果存在)
    • 请重试该请求
  • 在每个请求上,通过规范根URL迭代存储会话信息的所有领域:
    • 使用请求方法和路径计算H(A2)
    • 使用HA1、nonce(+可选nc、cnonce、qop)和HA2计算H(A3)
    • 构建
      Authorization
      头并将其添加到
      HttpUrlConnection
  • 实现某种会话修剪
通过使用
Authenticator
,您可以确保只要
HttpUrlConnection
本机支持摘要,您的代码就不再被使用(因为您首先不会收到401)

这只是一个如何实现它的快速总结,供您了解


如果您想更进一步,您可能还希望实现SHA256:

正确的是,
HttpUrlConnection
不支持摘要身份验证。如果您的客户端必须使用摘要进行身份验证,则您有几个选项:

  • 编写自己的HTTP摘要实现。若您知道需要对哪些服务器进行身份验证,并且可以忽略摘要规范中不需要的部分,那个么这是一个很好的选择。下面是实现摘要子集的示例:
  • 使用外部库,这是Android的摘要库。您可以使用它来解析摘要挑战并生成对它们的响应。它支持常见摘要用例和一些很少使用的用例,可以在
    HttpURLConnection
    之上使用
  • 与一起使用,这是一个向OkHttp添加Http摘要支持的插件。使用OkHttp支持摘要很容易,只需添加
    OkHttp摘要
    作为身份验证器,您将获得透明的Http摘要支持。如果您已经使用OkHtt
    try {   
            HttpClient client = new HttpClient(
                    new MultiThreadedHttpConnectionManager());
    
            client.getParams().setAuthenticationPreemptive(true);
            Credentials credentials = new UsernamePasswordCredentials("username", "password");
            client.getState().setCredentials(AuthScope.ANY, credentials);
            List<String> authPrefs = new ArrayList<String>(2);
            authPrefs.add(AuthPolicy.DIGEST);
            authPrefs.add(AuthPolicy.BASIC);
            client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,
                    authPrefs);
            GetMethod getMethod = new GetMethod("your_url");
            getMethod.setRequestHeader("Accept", "application/xml");
            client.executeMethod(getMethod);
            int status = getMethod.getStatusCode();
            getMethod.setDoAuthentication(true);
            System.out.println("status: " + status);
            if (status == HttpStatus.SC_OK) {
                String responseBody = getMethod.getResponseBodyAsString();
                String resp = responseBody.replaceAll("\n", " ");
                System.out.println("RESPONSE \n" + resp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    
    // requestMethod: "GET", "POST", "PUT" etc.
    // Headers: A map with the HTTP-Headers for the request
    // Data: Body-Data for Post/Put
    int statusCode = this.requestImpl(requestMethod, headers, data);
    if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED && hasUserNameAndPassword) {
        String auth = getResponseHeaderField("WWW-Authenticate");
        // Server needs Digest authetication
        if(auth.startsWith("Digest")){
              // Parse the auth Header
              HashMap<String, String> authFields = parseWWWAuthenticateHeader(auth);
              // Generate Auth-Value for request
              String requestAuth = generateDigestAuth(authFields);
              headers.put("Authorization", authStr);
              statusCode = this.requestImpl(requestMethod, headers, data);
        }
    }
    
        String digestAuthStr = null;
    
        String uri = getURL().getPath();
        String nonce = authFields.get("nonce");
        String realm = authFields.get("realm");
        String qop = authFields.get("qop");
        String algorithm = authFields.get("algorithm");
        String cnonce = generateCNonce();
        String nc = "1";
        String ha1 = toMD5DigestString(concatWithSeparator(":", username, realm, password));
        String ha2 = toMD5DigestString(concatWithSeparator(":", requestMethod, uri));
        String response = null;
        if (!TextUtils.isEmpty(ha1) && !TextUtils.isEmpty(ha2))
            response = toMD5DigestString(concatWithSeparator(":", ha1, nonce, nc, cnonce, qop, ha2));
    
        if (response != null) {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Digest ");
            sb.append("username").append("=\"").append(username).append("\", ");
            sb.append("realm").append("=\"").append(realm).append("\", ");
            sb.append("nonce").append("=\"").append(nonce).append("\", ");
            sb.append("uri").append("=\"").append(uri).append("\", ");
            sb.append("qop").append("=\"").append(qop).append("\", ");
            sb.append("nc").append("=\"").append(nc).append("\", ");
            sb.append("cnonce").append("=\"").append(cnonce).append("\"");
            sb.append("response").append("=\"").append(response).append("\"");
            sb.append("algorithm").append("=\"").append(algorithm).append("\"");
            digestAuthStr = sb.toString();
        }
    
    private static String generateCNonce() {
        String s = "";
        for (int i = 0; i < 8; i++)
            s += Integer.toHexString(new Random().nextInt(16));
        return s;
    }