Archive for the ‘REST’ category

Jersey 1.9.1 Released

September 16th, 2011

Over the past two weeks, I’ve been working with Pavel on finalizing and staging the bits for the hands-on-lab on OAuth, we are going to do at this year’s JavaOne. As part of that, I had to make a few more clean-ups in the Jersey OAuth client library, so we decided to make a branch for 1.9.1 and make those clean-ups along with some other small fixes there. Now, 2 weeks after 1.9, we released it. This is the release we’ll be using for JavaOne and although the release cycle was so short, it does have two nice additions worth highlighting.

  • Un-/marshalling collection types
    Until 1.9.1, JAXB un-/marshalling in Jersey worked only for Collection and List interfaces. I.e. if your resource method returned (or took as a parameter) Collection<Foo> or List<Foo> (where Foo was a JAXB bean), de-/serialization from/to XML/JSON would work, but if it returned LinkedList<Foo> or Set<Foo> or any other Collection subtype, it would not work. This is fixed in 1.9.1 and you can now return and retrieve any well-known interfaces that extend Collection (such as Set, Queue, etc.) and their implementations which have default public constructor.
  • PostReplaceFilter improvements
    PostReplaceFilter can be used to support clients which can’t send the full range of HTTP methods. It enables converting POST requests to other methods such as PUT or DELETE. If a POST request comes with a different method specified in X-HTTP-Method-Override header, the filter will replace POST in the request with that specified method. This has been in Jersey for a while, but only supported method overriding using the X-HTTP-Method-Override header. In 1.9.1 you can now use “_method” query parameter as well, and when overriding POST to GET the filter will convert all the form parameters to query parameters. Whether both header and query parameter are looked at by the filter (or only the header or only the query parameter) is configurable. Thanks to gk5885Fredy Nagy and Florian Hars for sharing their views and patches.
You can see the full list of changes in our changelog. For more info on Jersey see http://jersey.java.net.

Jersey and Cross-Site Request Forgery (CSRF)

September 14th, 2011

About two weeks back we released Jersey 1.9. See Jakub’s blog for more info on what’s new. One thing Jakub didn’t mention is that Jersey 1.9 also includes a new server side filter for Cross Site Request Forgery prevention. I won’t go into the details on what CSRF is – please refer to the OWASP CSRF page for that. Unfortunately, the generally recommended prevention is to generate per-request or per-session tokens on the server side, which client then has to include in its subsequent requests. This is quite easy to implement and there are servlet filters for doing that, however it does require a session state to be maintained and thus is not very RESTful. I was trying to implement something that would not require a session. After some searching I found the following two papers which both suggest there is a solution which works, and is not based on sessions:

The main idea is to check the presence of a custom header (agreed-upon between the server and a client – e.g. X-CSRF or X-Requested-By) in all state-changing requests coming from the client. The value of the header does not really matter. It works, because the browser would not send custom headers unless the web page makes a request using XMLHttpRequest, which only allows requests to the same site.

So, in Jersey 1.9 we added a server-side filter which does exactly that. You can find it here: server-side CsrfProtectionFilter.java

And, to make it easy to build clients, a corresponding client filter (that attaches the custom header to all potentially state-changing requests) is there as well: client-side CsrfProtectionFilter.

This can be further extended based on the feedback – we may add a check for the Referrer header and eventually even implement the session-based solution as an available configuration option. Just let us know, if you have an opinion.

Jersey Hands-On Lab

September 16th, 2009

Earlier this year, me and Naresh created an introductory level Jersey hands-on lab for JavaOne ’09. As I realized just recently, the hands-on labs had been made available for download to all SDN members (free registration) shortly after JavaOne. It may be another useful resource for you to get started with Jersey. The lab provides detailed step-by-step instructions on how to set up your environment and then guides you through 3 exercises:

  1. Hello world! – leading you through your first JAX-RS/Jersey application, explaining the JAX-RS basics
  2. Advanced JAX-RS/Jersey Features – showing how to develop a little more complex web application using JAX-RS/Jersey features such as path parameters, multiple representations for a resource, writing your own MessageBodyReader/Writer, Jersey MVC and some more
  3. Using Jersey Client API – showing how to access web resources using the Client API provided by Jersey

You can download the Hands-On Lab as well as get more info here. I’ve also added this link to our Jersey Wiki. After you download the lab, just unzip the file and open index.html in restwebservice directory. The zip also contains solution directories for all three exercises. I hope the lab will be of help. Let me know in case you have any questions or feedback on it.

Using OAuth Callback

August 17th, 2009

Over the past week I’ve been playing a bit with JavaFX. I will blog on my experience later. Today I would like to follow up on my last post and write about the OAuth callback.
If you look at the sample code in my last blog, there is one ugly thing – users have to be told to press Enter once they authenticate in a browser that the application opens:

    // open the browser at the authorization URL to let user authorize
    Desktop.getDesktop().browse(new URI(URL_AUTHORIZE +
            "?oauth_token=" + response.auth.token.id));
    // wait for the user to authenticate
    System.out.println("Once you authenticated with SmugMug and granted" +
            "permissions to this app, press Enter to continue.");
    System.in.read();

Although the OAuth specification mentions one can pass a callback URL to the authorization call, I had not been able to make it work with SmugMug until recently, when I ran into this post on the SmugMug API forum. The post says that instead of accepting the oauth_callback as the query parameter in the authorization request, SmugMug allows me to set the callback URL for my application by editing the properties of the API key I’ve received in the SmugMug control panel. So, I went ahead and tried to set it to “http://localhost:9097/oauth” and it worked! So I was able to get rid of that ugly waiting for users pressing the Enter key with the following trick:

    synchronized (App.class) {
        // create an instance of the lightweight HTTP server on port 9097
        HttpServer server = HttpServer.create(new InetSocketAddress(9097), 0);
        // assign a handler to "/oauth" context
        server.createContext("/oauth", new HttpHandler() {
            public void handle(HttpExchange t) throws IOException {
                // notify the app the user has authorized
                synchronized (App.class) {
                    App.class.notifyAll();
                }
                // send some descriptive page as a response
                t.sendResponseHeaders(200, 0);
                OutputStream os = t.getResponseBody();
                InputStream is = getClass().getResourceAsStream("authorize.html");
                int count;
                byte[] b = new byte[32000];
                while (0 < (count = is.read(b))) {
                    os.write(b, 0, count);
                }
                // close the streams
                os.close();
                is.close();

            }
        });
        server.setExecutor(null); // creates a default executor
        // start the server
        server.start();
        // open the authorization URI in the browser
        Desktop.getDesktop().browse(new URI(URL_AUTHORIZE +
                "?oauth_token=" + response.auth.token.id));
        // wait until the HttpHandler sends me "notify" signal
        App.class.wait();
        // user has authorized - stop the lightweight HTTP server
        server.stop(0);
    }

Basically, just before I send a request to the authorization URI, I start an instance of the lightweight HTTP server (present in JDK6 or you can download it here in case you are running on JDK5) which listens on http://localhost:9097/oauth, and then just wait for the callback request to come in. SmugMug, once the user authorizes, redirects the browser to this URL, which wakes up the suspended execution of my app and returns some descriptive HTML page to the user - something along the same lines as the original SmugMug page, e.g.:

Thank you for adding App to your Authorized Applications.

(You can de-authorize anytime via your control panel.)

Now, one drawback of the SmugMug approach of not allowing applications to specify the callback URL dynamically (in the request to the authorization URL) is that the port your application uses is pretty much hard-coded - you have to manually enter it (as part of the callback URL) in your API key properties on SmugMug website. That means, if the port your application wants to use is busy you have to fall back to the original approach.

Using Jersey Client OAuth Support with SmugMug

August 10th, 2009

Recently I decided to use SmugMug to store my photo galleries. They have an API people can use to access the features of the site programmatically and I noticed they added OAuth in the latest version. So, I thought I would give it a try and see if I can use the OAuth Support in Jersey to authorize and authenticate my client application with SmugMug. Turned out it works! Here is how to do it:

  1. First you have to request an API Key from SmugMug. You can do it here.
  2. They probably approve these automatically – mine was approved immediately and I got the key along with a “secret” (another number used as a consumer secret key in OAuth).
  3. Now you can create a new maven project, adding jersey-client, jersey-json, oauth-signature and oauth-client as the dependencies – here is a pom file snippet:
        <dependency>
          <groupId>com.sun.jersey</groupId>
          <artifactId>jersey-client</artifactId>
          <version>1.1.2-ea-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>com.sun.jersey</groupId>
          <artifactId>jersey-json</artifactId>
          <version>1.1.2-ea-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>com.sun.jersey.oauth</groupId>
          <artifactId>oauth-signature</artifactId>
          <version>1.1.2-ea-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>com.sun.jersey.oauth</groupId>
          <artifactId>oauth-client</artifactId>
          <version>1.1.2-ea-SNAPSHOT</version>
        </dependency>
  4. The first thing you need to do according to the OAuth spec is to get an unauthorized request token from the provider (SmugMug in this case). You will need the key and the secret you obtained in step 1. The SmugMug API provides a method for requesting these tokens – getRequestToken. Here is an example of how you can call this method using Jersey client API and the Jersey OAuth library:
    public class App {
        // base URL for the API calls
        private static final String URL_API =
                "http://api.smugmug.com/services/api/json/1.2.2/";
    
        private static final String CONSUMER_SECRET = /* your API Key */;
        private static final String CONSUMER_KEY = /* your secret key */;
    
        public static void main( String[] args ) throws Exception {
            // Create a Jersey client
            Client client = Client.create();
    
            // Create a resource to be used to make SmugMug API calls
            WebResource resource = client.resource(URL_API).
                    queryParam("method", "smugmug.auth.getRequestToken");
    
            // Set the OAuth parameters
            OAuthSecrets secrets = new OAuthSecrets().consumerSecret(CONSUMER_SECRET);
            OAuthParameters params = new OAuthParameters().consumerKey(CONSUMER_KEY).
                    signatureMethod("HMAC-SHA1").version("1.0");
            // Create the OAuth client filter
            OAuthClientFilter filter =
                    new OAuthClientFilter(client.getProviders(), params, secrets);
            // Add the filter to the resource
            resource.addFilter(filter);
    
            // make the request and print out the result
            System.out.println(resource.get(String.class));
        }
    }
  5. The next step in the OAuth flow is to obtain user authorization. To do this, the user needs to be redirected to the SmugMug authorization URL – http://api.smugmug.com/services/oauth/authorize.mg (see the SmugMug Specifics section on their OAuth page), passing the request token ID as a query parameter (you need to extract that from the getRequestToken method’s response). At this URL the user will log in and grant the requested access to your application. Here is how I did it:
    public class App {
        // base URL for the API calls
        private static final String URL_API =
                "http://api.smugmug.com/services/api/json/1.2.2/";
        // authorization URL
        private static final String URL_AUTHORIZE =
                "http://api.smugmug.com/services/oauth/authorize.mg";
    
        private static final String CONSUMER_SECRET = /* your API Key */;
        private static final String CONSUMER_KEY = /* your secret key */;
    
        public static void main( String[] args ) throws Exception {
            // Create a Jersey client
            Client client = Client.create();
    
            // Create a resource to be used to make SmugMug API calls
            WebResource resource = client.resource(URL_API).
                    queryParam("method", "smugmug.auth.getRequestToken");
    
            // Set the OAuth parameters
            OAuthSecrets secrets = new OAuthSecrets().consumerSecret(CONSUMER_SECRET);
            OAuthParameters params = new OAuthParameters().consumerKey(CONSUMER_KEY).
                    signatureMethod("HMAC-SHA1").version("1.0");
            // Create the OAuth client filter
            OAuthClientFilter filter =
                    new OAuthClientFilter(client.getProviders(), params, secrets);
            // Add the filter to the resource
            resource.addFilter(filter);
    
            // make the request
            RequestTokenResponse response = resource.get(RequestTokenResponse.class);
            // check the status
            if (!"ok".equals(response.stat)) {
                System.out.println("getRequestToken failed with response: " +
                        response.toString());
                return;
            }
    
            // open the browser at the authorization URL to let user authorize
            Desktop.getDesktop().browse(new URI(URL_AUTHORIZE +
                    "?oauth_token=" + response.auth.token.id));
        }
    }

    The RequestTokenResponse class representing getRequestToken method’s response looks as follows:

    @XmlRootElement
    public class RequestTokenResponse {
        public String stat;
        public String method;
        public @XmlElement(name="Auth") AuthElement auth;
    
        public static class AuthElement {
            public @XmlElement(name="Token") TokenElement token;
    
            @Override
            public String toString() {
                return "token=(" + (token == null ? "null" : token.toString()) + ")";
            }
        }
    
        public static class TokenElement {
            public String id;
            public @XmlElement(name="Secret") String secret;
    
            @Override
            public String toString() {
                return "id=" + id + " secret=" + secret;
            }
        }
    
        @Override
        public String toString() {
            return "stat=" + stat + " method=" + method + " auth=(" +
                    (auth == null ? "null" : auth.toString()) + ")";
        }
    }
  6. After the user authenticates and grants access for your application, the last step is to request an access token – that will then enable your application to make subsequent API calls. You can implement this by adding the following lines at the end of the main method from the previous bullet:
            // wait for the user to authenticate
            System.out.println("Once you authenticated with SmugMug and granted" +
                    "permissions to this app, press Enter to continue.");
            System.in.read();
    
            // make an API call to request the access token
            resource = client.resource(URL_API).queryParam("method",
                    "smugmug.auth.getAccessToken");
            // use the request token id and secret to create the request
            secrets.setTokenSecret(response.auth.token.secret);
            params.token(response.auth.token.id);
            resource.addFilter(filter);
            // make the request and print out the result
            System.out.println(resource.get(String.class));
  7. That’s it! Now your application can store the access token and use it to perform actions on behalf of the user.