JAX-WS Sharepoint 401 Unauthorized NTLM
Asked Answered
R

3

13

I try to access a Sharepoint list via JAX-WS as described here

However, when running the code below I get:

java.lang.Exception: Exception. See stacktrace.com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 401: Unauthorized

Sharepoint requires NTLM authentication. What may be the problem? Thanks a lot!

public static ListsSoap sharePointListsAuth(String userName, String password) throws Exception {
    ListsSoap port = null;
    if (userName != null && password != null) {
        try {
            Lists service = new Lists();
            port = service.getListsSoap();
            System.out.println("Web Service Auth Username: " + userName);
            ((BindingProvider) port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, userName);
            ((BindingProvider) port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
        } catch (Exception e) {
            throw new Exception("Error: " + e.toString());
        }
    } else {
        throw new Exception("Couldn't authenticate: Invalid connection details given.");
    }
    return port;
}
Relative answered 1/2, 2011 at 16:28 Comment(0)
N
20

I was facing the same problem when connecting with JAX-WS to Exchange web services, and here's what worked for me:

First, create an authenticator:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

public class NtlmAuthenticator extends Authenticator {

  private final String username;
  private final char[] password;

  public NtlmAuthenticator(final String username, final String password) {
    super();
    this.username = new String(username);
    this.password = password.toCharArray(); 
  }

  @Override
  public PasswordAuthentication getPasswordAuthentication() {
    return (new PasswordAuthentication (username, password));
  }
}

In your application, set up the authenticator as the default:

String username = "DOMAIN\\USERNAME";
String password = "PASSWORD"

NtlmAuthenticator authenticator = new NtlmAuthenticator(username, password);
Authenticator.setDefault(authenticator);

Note that I'm using method #2 for specifying the domain as described in the Java documentation.

Newland answered 13/5, 2011 at 16:8 Comment(7)
Is that only setups for entire application? What if I need to authenticate just particular request with that credentials?Ceil
You can tell which URL is requesting the credentials with getRequestingURL() inside your custom Authenticator class. So, you can return credentials accordingly.Corinthian
@MarcelLevy - Where does the code you provided fit in with the code in the OP's question? The link you provided is now dead...Enjoin
@Joe, Sorry about the lag, but I've updated the link: docs.oracle.com/javase/6/docs/technotes/guides/net/…Newland
@MarcelLevy what version of httpclient is your application using? also what jax-ws framework? I'm trying this with httpclient 4.1.3 and spring webservicetemplate and it fails when deployed to Linux machineGiorgi
JAX-WS 2.1.7, and I've never been able to get it to work with Apache's HttpClient.Newland
@MarcelLevy the only solution worked for me, with minimal changes. Thank you so much!!Coincidental
L
2

Based on my learnings, overriding the BindingProvider parameters does NOT set the required username and password. The simplest way to prove this is that there is no way to pass the domain name through the BP override.

I've seen multiple posts over the internet suggesting a way similar to Marcel Levy's suggestion in above to use an NTLM authenticator instance (Which is the way defined as per JAVA 6 documentation available from Oracle). But, this solution did not work for me (I was developing a standalone program independent of any application server logic).

I googled and tried a lot of solutions to this problem.. apparently the simplest code that worked is as below using the JCIFS library

    //Set the jcifs properties
    jcifs.Config.setProperty("jcifs.smb.client.domain", "domainname");
    jcifs.Config.setProperty("jcifs.netbios.wins", "xxx.xxx.xxx.xxx");
    jcifs.Config.setProperty("jcifs.smb.client.soTimeout", "300000"); // 5 minutes
    jcifs.Config.setProperty("jcifs.netbios.cachePolicy", "1200"); // 20 minutes
    jcifs.Config.setProperty("jcifs.smb.client.username", "username");
    jcifs.Config.setProperty("jcifs.smb.client.password", "password");

    //Register the jcifs URL handler to enable NTLM
    jcifs.Config.registerSmbURLHandler();

Apparently CXF 3.0 doesnt have a valid way of configuring the HTTP Client (4.3.x) with NTCredentials instance. Please refer to bug https://issues.apache.org/jira/browse/CXF-5671


By the way, if you have a simple message which needs to be transmitted, just use HTTP Client (I worked using 4.3.4.. not sure of the earlier versions) with NTCredentials Instance. That too did the magic for me.. The sample is as below:

    final NTCredentials ntCredentials = new NTCredentials("username", "Passworrd","destination", "domain");
    CredentialsProvider credsProvider = new BasicCredentialsProvider();

    credsProvider.setCredentials(AuthScope.ANY, ntCredentials);
    CloseableHttpClient httpclient = HttpClientBuilder.create()
                                        .setDefaultCredentialsProvider(credsProvider)
                                        .build();
Laud answered 23/6, 2014 at 14:3 Comment(0)
B
1

As far as I know, you can't do NTLM authentication through BindingProvider.

If you are familiar with Spring framework, you can use Spring-WS. Spring-WS supports transport with Apache HttpClient 4.x through the HttpComponentsMessageSender class. Apache HttpClient 4.x has good support for NTLM authentication. You can use the JAX-WS classes generated by wsimport tool as argument to marshalSendAndReceive.

Burdine answered 18/6, 2012 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.