0

I'm trying to use wink-client v1.4 to communicate with a Sharepoint RESTful web service. I have created a simple Java SE Maven project that can do this task on Windows using a BasicAuthSecurityHandler. However, this same project doesn't work on Mac OS X. I receive a 401 HTTP status code on the Mac. Wink is somehow using my NTLM credentials when being run from Windows. I'm using JDK 7 for both platforms.

How can I use NTLM authentication with Apache Wink client?

public String getSharepointInfo() {
    spUser = "user";
    spPassword = "password";
    spUri = "https://someSharepointURL/";

    ClientConfig clientConfig = new ClientConfig();

    Application app = new Application() {
        public Set<Class<?>> getClasses() {
            Set<Class<?>> classes = new HashSet<Class<?>>();
            classes.add(WinkMOXyJsonProvider.class);
            return classes;
        }
    };
    clientConfig.applications(app);
    BasicAuthSecurityHandler basicAuthSecurityHandler = new BasicAuthSecurityHandler();
    basicAuthSecurityHandler.setUserName(spUser);
    basicAuthSecurityHandler.setPassword(spPassword);
    clientConfig.handlers(basicAuthSecurityHandler);
    RestClient client = new RestClient(clientConfig);
    Resource resource = client.resource(spUri);

    ClientResponse response = resource.accept("*/*").get();

    String blah = response.getEntity(String.class);
    System.out.println("The response is " + blah);
    return blah.toString();
}
Chris Harris
  • 1,329
  • 4
  • 17
  • 28
  • I've gotten a GET request to work with NTLM authentication using Apache HttpClient v4.0.1 on OS X. I followed this link: http://zhangyelei.blogspot.com/2014/03/httpclient-version-conflict-on-was.html. This is a crucial first step, since that HttpClient version is used in WAS v8.0 and v8.5. To override that version, you have to go to ridiculous measures, such as an Isolated Shared Library. That may be unfeasible for some, as is my case. Now, I just need to figure out how to use Apache HttpClient v4.0.1 with Apache Wink. – Chris Harris May 01 '14 at 21:20

1 Answers1

0

I've figured it out.

My ultimate goal was to create a simple test case that I could port to WebSphere Application Server v8.0. The Apache Wink client can't handle NTLM authentication on its own. You have to use a separate Http client to handle NTLM authentication. I chose Apache Http Cient v4.0.1, since that buggy version is packaged in WAS v8.0. It's a huge pain to override that provided version too. That's why I didn't choose a more recent, better version of Apache HttpClient.

So, here's how you get Apache Http Client v4.0.1 to handle NTLM authentication: Use the following dependencies...

    <dependency>
        <groupId>jcifs</groupId>
        <artifactId>jcifs</artifactId>
        <version>1.3.17</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>org.apache.wink</groupId>
        <artifactId>wink-client</artifactId>
        <version>1.4</version>
    </dependency>

I'm using com.ibm.ws.prereq.jaxrs.jar contained in WAS v8.0 to get Apache Http Client v4.0.1. That's installed in my Maven repo, and I specify that as a dependency to get Http Client v4.0.1.

Follow the steps here.

Now, Wink comes into play:

public int attemptWinkHttpClienGET() {
    ClientResponse response = null;
    try {
        String spUri = "https://some-sharepoint-url/listdata.svc/";

        StringBuilder sb = new StringBuilder();
        sb.append(spUri).append("UserInformationList").toString();

        DefaultHttpClient httpClient = new DefaultHttpClient();
        httpClient.getAuthSchemes().register("ntlm",new JCIFSNTLMSchemeFactory());
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        NTCredentials ntcred = new NTCredentials("username_here", "password_here", InetAddress.getLocalHost().getHostName(), "domain_here");
        credsProvider.setCredentials(new AuthScope("base_url_here_sans_https://", 443, AuthScope.ANY_REALM, "NTLM"), ntcred);
        httpClient.setCredentialsProvider(credsProvider);

        org.apache.wink.client.ClientConfig httpClientConfig = new org.apache.wink.client.ApacheHttpClientConfig(httpClient);
        Application app = new Application() {
            public Set<Class<?>> getClasses() {
                Set<Class<?>> classes = new HashSet<Class<?>>();
                classes.add(WinkMOXyJsonProvider.class);
                return classes;
            }
        };
        httpClientConfig.applications(app);
        RestClient client = new RestClient(httpClientConfig);
        Resource resource = client.resource(sb.toString());
        response = resource.accept(MediaType.APPLICATION_JSON_TYPE).get();
        UserInformationListResponse blah = response.getEntity(UserInformationListResponse.class);
        Results[] results = blah.getD().getResults();
        for (Results result : results) {
            System.out.println("User Name: " + result.getFirstName() + " " + result.getLastName());
        }
        System.out.println("The response is " + response.getStatusCode());
        response.consumeContent();
    } catch (UnknownHostException ex) {
        Logger.getLogger(HttpTest.class.getName()).log(Level.SEVERE, null, ex);
    }

    return response.getStatusCode();
}

Now, the final bit. I use MOXy as my JAXB implementation. I had some issues getting it to work even though I was registering it in my app variable. I was seeing some Jackson-related errors. Apache HttpClient v4.0.1 is apparently using Jackons under the hood as a default. Here's what I did to overcome that problem.

I added the following dependencies:

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-moxy</artifactId>
        <version>2.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.4.0-rc2</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-jaxrs</artifactId>
        <version>1.9.13</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-xc</artifactId>
        <version>1.9.13</version>
    </dependency>

Here's WinkMOXyJsonProvider.java

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class WinkMOXyJsonProvider extends MOXyJsonProvider {

}

I observed the String result returned from Sharepoint and then created a bunch of MOXy POJO's mimicking the JSON object hierarchy.

Chris Harris
  • 1,329
  • 4
  • 17
  • 28