Question

I am developing an android application and want to connect to a REST service using URLConnection. The resource is protected using Digest Authentication. I can successfully access the REST service via the browser. I do not want to use HttpClient because URLConnection is the more current way to connect for the future.

But when I try to access the resource via the code below, I get a 401 Error with an java.io.FileNotFoundException. I have researched this thoroughly but no success, solutions appreciated.

Note: My REST service has been developed using Grails, I am running this code in the android emulator with code developed in Eclipse on the windows 64 bit OS.

CODE

URL myurl = new URL("http://10.0.2.2:8080/MyREST/customers");

HttpURLConnection myurlConnection = (HttpURLConnection) myurl.openConnection();

String basicAuth =  "Basic " + (Base64.encode(userpass.getBytes(),android.util.Base64.DEFAULT)).toString();
myurlConnection.setRequestProperty ("Authorization", basicAuth);

try {
    int responseCode1 = ((HttpURLConnection) myurlConnection).getResponseCode();
    Log.i("MyLongOperation", "Check connection" +Integer.toString(responseCode1) );

    InputStream in = new BufferedInputStream(myurlConnection.getInputStream());
    readStream(in);
}
finally {
   myurlConnection.disconnect();
  }     

I have also tried setting authentication at a global level with no effect

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(username, password.toCharArray());                                    
        }
    }
   );

I have referred to this article - with no success. Connecting to remote URL which requires authentication using Java

Was it helpful?

Solution

If the resource is protected by "Digest" then sending a "Basic" authorization scheme in your code will not work because the server would not recognize it.

Secondly, by using a "preemptive" authentication, setting the Authorization header w/o it being requested is kind of a security hole. You will be sending information to the server that it has not requested.

Thirdly, the "Authenticator.setDefault" most likely will not be requested as there was some significant back-and-forth caused by MicroSoft's implementation of HTTP digest authentication (YMMV may vary on my recollection of this). As such, Sun/Oracle decided to leave this behavior disabled by default as per this document.

That said, you may be better off looking into utilizing the Apache HTTP client bundled with Android to do this. There is a bundled implementation Digest Authentication included. There is an example of "preemptive" digest authentication located here.

Couple of caveats to be aware of:

  • Pay CLOSE attention to the "HttpHost" stored in "target" - this MUST MATCH EXACTLY the host name, protocol port, and protocol scheme used in the URL being retrieved.
  • The example provided is for HTTP Client 4.2.x. I am not 100% sure of the version included in Android but you should be able to locate working examples.

Update Submitter has provided additional comments with regard to the statement that it is recommended by Google to use the HttpURLConnection with articles here and here.

While I trust the statements made by Tim Bray with regard to the reasoning as to why you should be using the provided HttpURLConnection object for performing these calls, I do not agree that they should be immediately accepted on face value.

There is no indication as to the level of support of digest authentication provided by the implementation in Android. As I mentioned earlier, the HttpURLConnection does not support immediately as it has been known to be buggy.

If you are decided that you are going to use HTTP Digest Authentication, regardless of the fact that it has been deemed unstable by the majority of the community, I would attempt to set the following system properties in your application as EARLY as possible during the Android lifecycle:

  • http.auth.digest.validateServer="true"
  • http.auth.digest.validateProxy="true"

By doing so, this should enable the digest authentication scheme.

I am, again, going to re-iterate that the Apache HTTP Client bundled with Android was developed and designed specifically to address short-comings of the basic Java HttpURLConnection, providing much a much broader and robust client for dealing with HTTP(s) based data streams.

I would recommend trying a couple of things as well, see if you can configure your container to provide "Basic" authentication protection. The other, more complex option, would be to possibly provide X.509 Certificate Based authentication.

I hope that this clarification helps you get to your goal.

OTHER TIPS

change 'Base64.encode ...' to 'Base64.encodeToString ...'

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top