JAX-WS Sharepoint 401 Unauthorized NTLM

2019-01-14 03:38发布

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;
}

3条回答
女痞
2楼-- · 2019-01-14 04:07

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();
查看更多
Anthone
3楼-- · 2019-01-14 04:09

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.

查看更多
等我变得足够好
4楼-- · 2019-01-14 04:21

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.

查看更多
登录 后发表回答