AsyncHttpClient Authentication failed

ぃ、小莉子 提交于 2019-12-03 08:58:10
StarPinkER

From Basic Authentication with Android:

Android is shipping with Apache's HttpClient 4.0 Beta2, which has a pitfall, when it comes to Basic Authentication.

When you search for HttpClient and Basic Authentication, Google will most definitely send you to the official documentation of HttpClient 3.x, which shows you, how to do Basic Authentication in a preemptive way. That means, sending the client's credentials with every request, instead of waiting for a 401 Unauthorized response and only then sending the credentials. That's probably what you want to in the first place, because it saves your client a request.

HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(new AuthScope("myhost", 80, AuthScope.ANY_REALM), defaultcreds);

This sample code won't compile with HttpClient version 4. The method called setAuthenticationPreemptive is missing. The problem is, if you omit this very method call, the code still works, but the authentication is not preemptive. We missed this little detail and only noticed after a while, that every request was preceded by a 401 Unauthorized request/response cycle. That doubled the amount of requests we served.

Now check the implementation of AsyncHttpClient:

private final DefaultHttpClient httpClient;
public void setBasicAuth( String user, String pass, AuthScope scope){
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user,pass);
    this.httpClient.getCredentialsProvider().setCredentials(scope, credentials);
}

So I think loopj might also run into the problem of using the old HttpClient 3.x way of doing Basic Authentication. It does work, but it's not preemptive.

A straightforward solution will be download the source code of loopj and modify its source and use the modified version.

Modification in code would be:

    httpClient.setBasicAuth(ApplicationConstants.userName,
            ApplicationConstants.password);

instead of

            httpClient.setBasicAuth(ApplicationConstants.userName,
            ApplicationConstants.password, new AuthScope(
                    "http://*.*.*.*:8080/uploadservice", 80,
                    AuthScope.ANY_REALM));

I came up with another solution, because I was having issues with in-between-proxies that didn't like how the Authorization header was built by using the above proposed solution and also the default (buggy) setBasicAuth(username, password) implementation of AsyncHttpClient.

So I added the header myself, doing the BASE64 username/password encoding myself, but using the addHeader() method inherited from the parent client implementation.

It works now.

asyncHttpClient.addHeader(
  "Authorization",
    "Basic " + Base64.encodeToString(
      (username+":"+password).getBytes(),Base64.NO_WRAP)
);

I hope this helps somebody.

I guess you can resolve this issue by explicitly adding the header which handle Basic Auth.

Context context = this;
String url = "http://*.*.*.*:8080/someurl";

UsernamePasswordCredentials credentials = 
new UsernamePasswordCredentials(ApplicationConstants.userName,
            ApplicationConstants.password);

Header header = BasicScheme.authenticate(credentials, "UTF-8", false);

Header[] headers = {header};

RequestParams params = new RequestParams();

httpClient.get(context, url, headers, params, new AsyncHttpResponseHandler(){ ..

Also don't forget to override the "onComplete" method as well if you want to know what was the server response. Because in case of exception it might not go through "onSuccess" and "onFailure"..

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!