4

Is there any way to implement NTLM Authentication with HttpURLConnection? Currently I have implemented it with DefaultHttpClient and JCIFSEngine for the authentication scheme. ( My inspiration was : Android: NTLM Authentication, ksoap, and persistent connections)

But since Android 6 Apache HTTP Client Removal, I was looking for a solution besides adding useLibrary 'org.apache.http.legacy' in app gradle file, cause I want to improve my code using HttpURLConnection class instead. As documentation says, this API is more efficient because it reduces network usage through transparent compression and response caching, and minimizes power consumption.

Community
  • 1
  • 1
Iulia Barbu
  • 1,522
  • 12
  • 25

1 Answers1

6

HttpURLConnection can work with NTLM only if you add library jcifs.

This example works with latest jcifs-1.3.18 :

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.impl.auth.NTLMEngineException;

public class TestNTLMConnection {
    public static void main(String[] args) throws UnknownHostException, IOException, NTLMEngineException {
        // Method 1 : authentication in URL
        jcifs.Config.registerSmbURLHandler();
        URL urlRequest = new URL("http://domain%5Cuser:pass@127.0.0.1/");

        // or Method 2 : authentication via System.setProperty()
        // System.setProperty("http.auth.ntlm.domain", "domain");
        // System.setProperty("jcifs.smb.client.domain", "domain");
        // System.setProperty("jcifs.smb.client.username", "user");
        // System.setProperty("jcifs.smb.client.password", "pass");
        // Not verified // System.setProperty("jcifs.netbios.hostname", "host");
        // System.setProperty("java.protocol.handler.pkgs", "jcifs");
        // URL urlRequest = new URL("http://127.0.0.1:8180/simulate_get.php");

        HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();

        StringBuilder response = new StringBuilder();

        try {
            InputStream stream = conn.getInputStream();
            BufferedReader in = new BufferedReader(new InputStreamReader(stream));

            String str = "";
            while ((str = in.readLine()) != null) {
                response.append(str);
            }
            in.close();   

            System.out.println(response);
        } catch(IOException err) {
            System.out.println(err);
        } finally {
            Map<String, String> msgResponse = new HashMap<String, String>();

            for (int i = 0;; i++) {
                String headerName = conn.getHeaderFieldKey(i);
                String headerValue = conn.getHeaderField(i);
                if (headerName == null && headerValue == null) {
                    break;
                }
                msgResponse.put(headerName == null ? "Method" : headerName, headerValue);
            }

            System.out.println(msgResponse);
        }
    }
}

Warning: jcifs ignores the connectTimeout and readTimeout you define with the library, it's the reason why the connection takes ages to break when the host is not responding. Use the code I describe in this SO thread to avoid this bug.

Community
  • 1
  • 1
ron190
  • 1,032
  • 1
  • 17
  • 29
  • I had to use jcifs.Config.setProperty() instead of System.setProperty(). It seems like properties set by System.setProperty() are only used if the file jcifs.properties exists or if one of the methods Config.load() or Config.setProperties() are called directly. – Henrik Solgaard Sep 19 '19 at 18:16
  • It was also an issue that the server responded with 411 (Length Required) because the Content-Length header was not set. I had to call conn.setRequestProperty("Content-Length", "0") and also run the JVM with -Dsun.net.http.allowRestrictedHeaders=true. I suppose this is a problem only with some Java versions and some servers. Not strictly related to the question, but I guess someone else who read this may run into the same problem. – Henrik Solgaard Sep 19 '19 at 18:37
  • Another tip: If there is no domain, it must be set to "", because the default is null, which will cause a NullPointerException inside jcifs. – Henrik Solgaard Sep 19 '19 at 18:45