Java Android中使用HttpURLConnection的摘要身份验证
正如问题allready所说,我正在尝试在android中进行摘要身份验证。Java Android中使用HttpURLConnection的摘要身份验证,java,android,httpurlconnection,digest-authentication,Java,Android,Httpurlconnection,Digest Authentication,正如问题allready所说,我正在尝试在android中进行摘要身份验证。 到目前为止,我已经使用了DefaultHttpClient及其身份验证方法(使用UsernamePasswordCredentials等),但自Android 5以来,它已被弃用,并将在Android 6中删除。 因此,我将从DefaultHttpClient切换到HttpUrlConnection 现在,我正在尝试实现摘要身份验证,其工作应该非常简单,如下所述: 但是由于某种原因,getPasswordAuthent
到目前为止,我已经使用了
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或未定义(可选选择
qop选项),否则忽略质询并转到下一个标题auth
- 使用
验证器获取凭据。requestPasswordAuthentication
- 使用用户名、域和密码计算H(A1)
- 存储规范的根URL、领域、HA1、用户名、nonce(+可选算法、不透明和客户端选择的qop选项,如果存在)
- 请重试该请求
- 检查算法是否为MD5或未定义(可选选择
- 在每个请求上,通过规范根URL迭代存储会话信息的所有领域:
- 使用请求方法和路径计算H(A2)
- 使用HA1、nonce(+可选nc、cnonce、qop)和HA2计算H(A3)
- 构建
头并将其添加到Authorization
HttpUrlConnection
- 实现某种会话修剪
Authenticator
,您可以确保只要HttpUrlConnection
本机支持摘要,您的代码就不再被使用(因为您首先不会收到401)
这只是一个如何实现它的快速总结,供您了解
如果您想更进一步,您可能还希望实现SHA256:正确的是,
HttpUrlConnection
不支持摘要身份验证。如果您的客户端必须使用摘要进行身份验证,则您有几个选项:
- 编写自己的HTTP摘要实现。若您知道需要对哪些服务器进行身份验证,并且可以忽略摘要规范中不需要的部分,那个么这是一个很好的选择。下面是实现摘要子集的示例:
- 使用外部库,这是Android的摘要库。您可以使用它来解析摘要挑战并生成对它们的响应。它支持常见摘要用例和一些很少使用的用例,可以在
之上使用HttpURLConnection
- 与一起使用,这是一个向OkHttp添加Http摘要支持的插件。使用OkHttp支持摘要很容易,只需添加
作为身份验证器,您将获得透明的Http摘要支持。如果您已经使用OkHttOkHttp摘要
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; }