users@jersey.java.net

Apache HTTP client support in repo <was> Re: [Jersey] Basic Auth on the client side

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Mon, 02 Feb 2009 14:33:24 +0100

Hi,

The Jersey client using the Apache HTTP client is now in the repo:

http://download.java.net/maven/2/com/sun/jersey/contribs/jersey-apache-client/1.0.2-SNAPSHOT/

I need to improve on the JavaDoc.

Below is a unit test for authentication that should give you an idea
on how to use it.

Paul.



package com.sun.jersey.client.apache.impl;

import javax.ws.rs.Path;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.container.filter.LoggingFilter;
import com.sun.jersey.api.core.DefaultResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.client.apache.ApacheHttpClient;
import com.sun.jersey.client.apache.config.ApacheHttpClientConfig;
import
com.sun.jersey.client.apache.config.DefaultApacheHttpClientConfig;
import com.sun.jersey.spi.resource.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScheme;
import
org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
import org.apache.commons.httpclient.auth.CredentialsProvider;

/**
  *
  * @author Paul.Sandoz_at_Sun.Com
  */
public class AuthTest extends AbstractGrizzlyServerTester {

     public AuthTest(String testName) {
         super(testName);
     }

     @Path("/")
     public static class PreemptiveAuthResource {
         @GET
         public String get(@Context HttpHeaders h) {
             String value =
h.getRequestHeaders().getFirst("Authorization");
             assertNotNull(value);
             return "GET";
         }
     }

     public void testPreemptiveAuth() {
         ResourceConfig rc = new
DefaultResourceConfig(PreemptiveAuthResource.class);
          
rc
.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
                 LoggingFilter.class.getName());
         startServer(rc);

         DefaultApacheHttpClientConfig config = new
DefaultApacheHttpClientConfig();
         config.getState().setCredentials(null, null, -1, "name",
"password");
          
config
.getProperties
().put(ApacheHttpClientConfig.PROPERTY_PREEMPTIVE_AUTHENTICATION, true);
         ApacheHttpClient c = ApacheHttpClient.create(config);

         WebResource r = c.resource(getUri().build());
         assertEquals("GET", r.get(String.class));
     }


     @Path("/test")
     @Singleton
     public static class AuthResource {
         int requestCount = 0;
         @GET
         public String get(@Context HttpHeaders h) {
             requestCount++;
             String value =
h.getRequestHeaders().getFirst("Authorization");
             if (value == null) {
                 assertEquals(1, requestCount);
                 throw new
WebApplicationException(Response.status(401).header("WWW-
Authenticate", "Basic realm=\"WallyWorld\"").build());
             } else {
                 assertTrue(requestCount > 1);
             }

             return "GET";
         }
     }

     public void testAuth() {
         ResourceConfig rc = new
DefaultResourceConfig(AuthResource.class);
          
rc
.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
                 LoggingFilter.class.getName());
         startServer(rc);

         DefaultApacheHttpClientConfig config = new
DefaultApacheHttpClientConfig();
         config.getState().setCredentials(null, null, -1, "name",
"password");
         ApacheHttpClient c = ApacheHttpClient.create(config);

         WebResource r = c.resource(getUri().path("test").build());
         assertEquals("GET", r.get(String.class));
     }

     static class BasicCredentialsProvider implements
CredentialsProvider {
         private boolean called;

         public Credentials getCredentials(AuthScheme arg0, String
arg1, int arg2, boolean arg3) throws CredentialsNotAvailableException {
             called = true;
             return new UsernamePasswordCredentials ("name",
                                                     "password");
         }

         boolean isCalled() {
             return called;
         }
     }

     public void testAuthInteractive() {
         ResourceConfig rc = new
DefaultResourceConfig(AuthResource.class);
          
rc
.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
                 LoggingFilter.class.getName());
         startServer(rc);

         DefaultApacheHttpClientConfig config = new
DefaultApacheHttpClientConfig();
          
config
.getProperties().put(ApacheHttpClientConfig.PROPERTY_INTERACTIVE, true);
         BasicCredentialsProvider bcp = new BasicCredentialsProvider();
          
config
.getProperties
().put(ApacheHttpClientConfig.PROPERTY_CREDENTIALS_PROVIDER, bcp);
         ApacheHttpClient c = ApacheHttpClient.create(config);

         WebResource r = c.resource(getUri().path("test").build());
         assertEquals("GET", r.get(String.class));
         assertTrue(bcp.isCalled());
     }

}


On Jan 28, 2009, at 6:51 PM, Matthieu Riou wrote:

> On Wed, Jan 28, 2009 at 9:16 AM, Steve Sims <steve_at_steveandgeorge.co.uk
> > wrote:
> Hi Matthieu,
>
> I originally used java.net.Authenticator.setDefault() but had
> problems setting it to null after each test (as in it would continue
> to send the same credentials). The other way to do it, as detailed
> in the thread that you mention, is to add a filter to the Jersey
> client which will create the HTTP Authorization header in the
> outgoing message which is required for Basic authentication.
>
> The following code snippet requires the Apache Commons Digest
> library to do the Base64 encoding:
>
> class BasicAuthenticationFilter extends ClientFilter {
>
> private String username;
> private String password;
>
> public BasicAuthenticationFilter(String username, String password) {
> this.username = username;
> this.password = password;
> }
>
> @Override
> public ClientResponse handle(ClientRequest cr) throws
> ClientHandlerException {
> if (null != username && null != password) {
> byte[] unencoded = (username + ":" +
> password).getBytes(Charset.forName("UTF-8"));
> byte[] cred = new Base64().encode(unencoded);
> String credString = new String(cred);
> String authHeader = "Basic " + credString;
> cr.getMetadata().add("authorization", authHeader);
> }
> // Call the next client handler in the filter chain
> return this.getNext().handle(cr);
> }
> }
>
> and then you just add it to the client like so:
>
> Client client = Client.create();
> ClientFilter authFilter = new BasicAuthenticationFilter("userid",
> "password);
> client.addFilter(authFilter);
>
> There's a minor issue (#196) which was fixed today in the trunk
> (thanks Paul!) which means that client.removeFilter(authFilter) will
> throw an exception, but then I'm only removing the filter because in
> some tests I wanted to change the credentials. I just worked around
> it by calling client.removeAllFilters() because that was the only
> filter I had attached.
>
> Obviously the above filter is pretty rough and won't do everything
> such as only sending the credentials when requested by the server
> etc. but it's something else that may fit the bill if
> Authenticator.setDefault() doesn't quite work.
>
> Thanks Steve, much appreciated. I think I'll still try to build a
> client from the current SVN content but if I can't make it work I'll
> fall back to your approach.
>
> Cheers,
> Matthieu
>
>
> Hope this helps,
>
> Steve
>
>
> Paul Sandoz wrote:
>
> On Jan 28, 2009, at 5:22 PM, Matthieu Riou wrote:
>
> Hi,
>
> I was wondering what were the options to do Basic Auth with the
> Jersey client in 1.0.1. I've seen an e-mail thread dating back from
> November where a patch had been proposed but it doesn't seem it's
> been integrated in 1.0.1 or maybe in a different form. I've been
> staring at the Javadoc for quite some time but couldn't find
> anything that looked like auth methods.
>
>
> Currently the only way is to use the java.net.Authenticator [1]
> because by default HttpURLConnection is utilized. Which is not ideal
> because it is a static, per JVM, configuration.
>
> We are working on support using the Apache HTTP client where basic
> auth can be set up on a per client basis. I hope this can make the
> 1.0.2 release, i just need to work on the implementation to ensure
> it is thread safe.
>
> Paul.
>
> [1] http://java.sun.com/javase/6/docs/technotes/guides/net/http-auth.html
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>
>