Categories
Development Java

NTLM mit Java

Um Daten von einem bestimmten Webservice beziehen zu können, muss der Aufruf durch einen authentifizierten User erfolgen. Der Webservice steht im Intranet des Kunden und der Aufruf im Browser funktioniert ohne Authentifizierung, denn im Hintergrund wird der Windows User übermittelt.

Der Aufruf über Java funktioniert nicht, da kein User übermittelt wird.

Die Authentifizierung am Webservice erfolgt lt. Ansprechpartner durch eine Windows Authentifizierung, was technischer ausgedrückt NTLM sein sollte.

Test Projekt aufsetzen

Um den Zugang zu testen ohne dabei den ganzen Ballast der großen Anwendung mitschleppen zu müssen, wird ein neues Projekt zum Testen aufgesetzt.

Das Projekt wird auf Java 8 konfiguriert und kommt mit einer einzigen Abhängigkeit aus: Dem Apache HTTPClient 4.5

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

Test 1

public static void test01() throws Exception {

  DefaultHttpClient httpclient = new DefaultHttpClient();
  httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
  httpclient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
                                                     new NTCredentials(username, password, "", ""));
  HttpGet httpGet = new HttpGet(url);
  httpGet.addHeader("accept", "application/json;odata=verbose");
  httpGet.addHeader("content-Type", "application/json;odata=verbose");
  httpGet.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
  // HttpResponse response = httpclient.execute(httpGet);
  // System.out.println("Responseeee" + response.getStatusLine());
  try (CloseableHttpResponse response2 = httpclient.execute(httpGet)) {
    StatusLine statusLine = response2.getStatusLine();
    System.out.println(statusLine.getStatusCode() + " " + statusLine.getReasonPhrase());
    String responseBody = EntityUtils.toString(response2.getEntity(), StandardCharsets.UTF_8);
    System.out.println("Response body: " + responseBody);
  }
}

Der Code funktioniert, wirft aber noch eine WARNING mit aus:

Mai 30, 2022 4:15:35 PM org.apache.http.client.protocol.RequestTargetAuthentication process 
WARNING: NEGOTIATE authentication error: No valid credentials provided (Mechanism level: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt))
200 OK
Response body: [{"Vkorg":"","VkorgDesc":"TEST Korea Limited"}]

Test 2

    public static void test02() throws Exception {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(18);
        cm.setDefaultMaxPerRoute(6);

        RequestConfig requestConfig = RequestConfig.custom()
        .setSocketTimeout(30000)
        .setConnectTimeout(30000)
        .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
        .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
        .build();

        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new NTCredentials(username, password, "", ""));

        // Finally we instantiate the client. Client is a thread safe object and can be used by several threads at the same time. 
        // Client can be used for several request. The life span of the client must be equal to the life span of this EJB.
        CloseableHttpClient httpclient = HttpClients.custom()
        .setConnectionManager(cm)
        .setDefaultCredentialsProvider(credentialsProvider)
        .setDefaultRequestConfig(requestConfig)
        .build();
        
        HttpGet httpGet = new HttpGet(url);            
        // HttpClientContext is not thread safe, one per request must be created.
        HttpClientContext context = HttpClientContext.create();    
        try ( CloseableHttpResponse response = httpclient.execute(httpGet, context) ) {
            StatusLine statusLine = response.getStatusLine();
            System.out.println(statusLine.getStatusCode() + " " + statusLine.getReasonPhrase());
            String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            System.out.println("Response body: " + responseBody);
        }
    }

Der Code funktioniert und wirft keine Warnung mehr aus.