在安卓设备上进行带有授权的HTTP POST请求

17

当我使用HttpPost的setHeader方法设置"Authorization"标头时,主机名从请求中消失,且始终返回400(错误请求)。在纯Java上(不使用Android)相同的代码可以正常工作,当我在Android上删除设置"Authorization"头部时,它也可以正常工作,但我需要授权。以下是代码(域名已更改):

HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://myhost.com/test.php");
post.setHeader("Accept", "application/json");
post.setHeader("User-Agent", "Apache-HttpClient/4.1 (java 1.5)");
post.setHeader("Host", "myhost.com");
post.setHeader("Authorization",getB64Auth());
List <NameValuePair> nvps = new ArrayList <NameValuePair>();
nvps.add(new BasicNameValuePair("data[body]", "test"));
AbstractHttpEntity ent=new UrlEncodedFormEntity(nvps, HTTP.UTF_8);
ent.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
ent.setContentEncoding("UTF-8");
post.setEntity(ent);
post.setURI(new URI("http://myhost.com/test.php"));
HttpResponse response =client.execute(post);

getB64Auth()方法返回Base64编码后的"login:password",例如:"YnxpcYRlc3RwMTulHGhlSGs=",但这不重要。

当使用纯Java调用以上代码时,这是lighttpd的error.log的一部分:

2011-02-23 15:37:36: (request.c.304) fd: 8 request-len: 308
POST /test.php HTTP/1.1
Accept: application/json
User-Agent: Apache-HttpClient/4.1 (java 1.5)
Host: myhost.com
Authorization: Basic YnxpcYRlc3RwMTulHGhlSGs=
Content-Length: 21
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Encoding: UTF-8
Connection: Keep-Alive

HTTP/1.1 200 OK
Content-type: text/html
Transfer-Encoding: chunked

并且记录来自 access.log 的记录(IP 已更改):

1.1.1.1 myhost.com - [23/Feb/2011:15:37:36 +0100] "POST /test.php HTTP/1.1" 200 32 "-" "Apache-HttpClient/4.1 (java 1.5)"

同样的代码在 Android 上调用时,我在日志中得到以下信息:

POST /test.php HTTP/1.1
Accept: application/json
User-Agent: Apache-HttpClient/4.1 (java 1.5)
Host: myhost.com
Authorization: Basic YnxpcYRlc3RwMTulHGhlSGs=

Content-Length: 21
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Encoding: UTF-8
Connection: Keep-Alive
Expect: 100-Continue


2011-02-23 15:45:10: (response.c.128) Response-Header:
HTTP/1.1 400 Bad Request
Content-Type: text/html
Content-Length: 349
Connection: close

访问日志:

1.1.1.1 - - [23/Feb/2011:15:45:10 +0100] "POST /test.php HTTP/1.1" 400 349 "-" "Apache-HttpClient/4.1 (java 1.5)"

如何在Android上使用POST请求获取授权? 当我使用HttpURLConnection而不是HttpClient时没有任何区别。


5
在授权头和内容长度头之间是否有额外的行? - Samuh
是的,有一行额外的代码,但只有当请求来自Android时才会出现。 - koral
2个回答

57

感谢Samuh的提示 :) 在GET请求中插入了多余的换行符,但是在POST请求中却很重要。 这是在Android中生成授权头的正确方式(在此情况下的getB64Auth函数):

 private String getB64Auth (String login, String pass) {
   String source=login+":"+pass;
   String ret="Basic "+Base64.encodeToString(source.getBytes(),Base64.URL_SAFE|Base64.NO_WRAP);
   return ret;
 }

缺少Base64.NO_WRAP标志。


4
这救了我,我之前用的是base64.default,改成Base64.URL_SAFE|Base64.NO_WRAP后就好了。 - Raluca Lucaci
太棒了!有人能解释一下为什么在POST请求中换行符很重要,但在GET请求中却不重要吗? - QuantumLicht
@Koral:您的解决方案对认证挑战确实帮助很大。 - Sudheer Kumar Palchuri
1
谢谢,我的朋友。Base64.URL_SAFE|Base64.NO_WRAP解决了问题。 - Schäfer

3
使用以下简单代码:
String authorizationString = "Basic " + Base64.encodeToString(
                        ("your_login" + ":" + "your_password").getBytes(),
                        Base64.NO_WRAP); //Base64.NO_WRAP flag
                post.setHeader("Authorization", authorizationString);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接