JAX-RS 2.0 Security Tutorial in JavaEE and JBoss
Tech-Today

JAX-RS 2.0 Security Tutorial in JavaEE and JBoss


This tutorial will summarize how the author was able to call a secured rest webservice using resteasy. We will not go into detail on how we build the entire project since the code is already pushed at github. Basically we will just note down the most important part of the process:

Note that our project was based on the linked in the reference below, we just made some modifications so that it will work on a newer version of jboss.

Tech Stack

  1. JavaEE6 / 7
  2. JBoss EAP 6.2

Things to remember

  1. Download resteasy-jaxrs-3.0.6.Final-all.zip
    1. Extract the zipped file and inside it find the folder: resteasy-jboss-modules-3.0.6.Final
    2. Copy all the folders inside it and paste into JBOSS_HOME/modules.
  2. In web.xml, we don't add any resteasy related parameters, instead it should look like this.
  3. <?xml version="1.0" encoding="UTF-8"?>

    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>JAX-RS 2.0 Security Demo</display-name>

    </web-app>
  4. Create a web service activator.
  5. package com.kalidadbiz;

    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;

    /**
    * @author Edward P. Legaspi
    **/
    @ApplicationPath("/api/rest")
    public class JaxRsActivator extends Application {

    }
  6. And finally the rest request interceptor:
  7. package com.kalidadbiz;

    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import java.util.StringTokenizer;

    import javax.annotation.security.DenyAll;
    import javax.annotation.security.PermitAll;
    import javax.annotation.security.RolesAllowed;
    import javax.ws.rs.container.ContainerRequestContext;
    import javax.ws.rs.core.MultivaluedMap;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.ext.ExceptionMapper;
    import javax.ws.rs.ext.Provider;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.jboss.resteasy.core.Headers;
    import org.jboss.resteasy.core.ResourceMethodInvoker;
    import org.jboss.resteasy.core.ServerResponse;
    import org.jboss.resteasy.util.Base64;

    /**
    * @author Edward P. Legaspi
    *
    * http://java.dzone.com/articles/java-ee-7-and-jax-rs-20
    **/
    @Provider
    public class RESTSecurityInterceptor implements
    javax.ws.rs.container.ContainerRequestFilter,
    ExceptionMapper<Exception> {

    private Log log = LogFactory.getLog(RESTSecurityInterceptor.class);

    private static final String AUTHORIZATION_PROPERTY = "Authorization";
    private static final String AUTHENTICATION_SCHEME = "Basic";
    private static final ServerResponse ACCESS_DENIED = new ServerResponse(
    "Access denied for this resource", 401, new Headers<Object>());;
    private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse(
    "Nobody can access this resource", 403, new Headers<Object>());;
    private static final ServerResponse SERVER_ERROR = new ServerResponse(
    "INTERNAL SERVER ERROR", 500, new Headers<Object>());

    @Override
    public void filter(ContainerRequestContext requestContext) {
    log.info("filter");

    ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) requestContext
    .getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
    Method method = methodInvoker.getMethod();
    // Access allowed for all
    if (!method.isAnnotationPresent(PermitAll.class)) {
    // Access denied for all
    if (method.isAnnotationPresent(DenyAll.class)) {
    requestContext.abortWith(ACCESS_FORBIDDEN);
    return;
    }

    // Get request headers
    final MultivaluedMap<String, String> headers = requestContext
    .getHeaders();

    // Fetch authorization header
    final List<String> authorization = headers
    .get(AUTHORIZATION_PROPERTY);

    // If no authorization information present; block access
    if (authorization == null || authorization.isEmpty()) {
    requestContext.abortWith(ACCESS_DENIED);
    return;
    }

    // Get encoded username and password
    final String encodedUserPassword = authorization.get(0)
    .replaceFirst(AUTHENTICATION_SCHEME + " ", "");

    // Decode username and password
    String usernameAndPassword = null;
    try {
    usernameAndPassword = new String(
    Base64.decode(encodedUserPassword));
    } catch (IOException e) {
    requestContext.abortWith(SERVER_ERROR);
    return;
    }

    // Split username and password tokens
    final StringTokenizer tokenizer = new StringTokenizer(
    usernameAndPassword, ":");
    final String username = tokenizer.nextToken();
    final String password = tokenizer.nextToken();

    // Verifying Username and password
    log.info(username);
    log.info(password);

    // Verify user access
    if (method.isAnnotationPresent(RolesAllowed.class)) {
    RolesAllowed rolesAnnotation = method
    .getAnnotation(RolesAllowed.class);
    Set<String> rolesSet = new HashSet<String>(
    Arrays.asList(rolesAnnotation.value()));

    // Is user valid?
    if (!isUserAllowed(username, password, rolesSet)) {
    requestContext.abortWith(ACCESS_DENIED);
    return;
    }
    }
    }
    }

    private boolean isUserAllowed(final String username, final String password,
    final Set<String> rolesSet) {
    boolean isAllowed = false;

    // Step 1. Fetch password from database and match with password in
    // argument
    // If both match then get the defined role for user from database and
    // continue; else return isAllowed [false]
    // Access the database and do this part yourself
    // String userRole = userMgr.getUserRole(username);
    String userRole = "ADMIN";

    // Step 2. Verify user role
    if (rolesSet.contains(userRole)) {
    isAllowed = true;
    }
    return isAllowed;
    }

    @Override
    public Response toResponse(Exception exception) {
    // TODO Auto-generated method stub
    return null;
    }

    }
  8. To test if the filter will really be triggered when there is a rest web service request, I've provided an action bean and a jersey client that sends a request with username and password
package com.kalidadbiz;

import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

/**
* @author Edward P. Legaspi
**/
public class RestClient {
private Log log = LogFactory.getLog(RestClient.class);

private String host;
private String api;
private Properties properties = new Properties();

public RestClient() {

}

public RestClient(String host, String api) {
this.host = host;
this.api = api;
}

public void addParam(String key, String value) {
properties.put(key, value);
}

public String execute() {
try {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("edward", "edward"));

String params = "";
if (properties != null) {
for (String key : properties.stringPropertyNames()) {
String value = properties.getProperty(key);
if (params != null) {
params += "&";
}
params += key + "=" + value;
}
}

String apiUrl = host + "/" + api;
if (params != null && params.length() > 0) {
apiUrl = apiUrl + "?" + params;
}
WebResource webResource = client.resource(apiUrl);

ClientResponse response = webResource.accept("application/json")
.get(ClientResponse.class);

if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}

return response.getEntity(String.class);
} catch (Exception e) {
log.error(e.getMessage());
return "";
}
}

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public String getApi() {
return api;
}

public void setApi(String api) {
this.api = api;
}
}

And lastly, don't forget to encrypt your password :-).

Github Repository

https://github.com/czetsuya/JAX-RS-REST-Security

References:

http://howtodoinjava.com/2013/07/25/jax-rs-2-0-resteasy-3-0-2-final-security-tutorial/





- Social Login Using Rest Fb
This is an implementation tutorial on how we can use REST FB to enable facebook social login on our web application. Basically, it's a project created from javaee7-war template. To run this app you need to set up a Facebook application with callback...

- How To Call A Javaee Rest Web Service With Basic Authentication Using Jquery Ajax
I don't really remember when I coded it, nor where I got it but I'm writing it here for future use :-) Below is the code I use to test CORS, http://en.wikipedia.org/wiki/Cross-origin_resource_sharing. <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>...

- How To Send And Receive Stomp Message In Jboss 7.2
The following code is an example of how we can send and receive a stomp message with JBoss 7.2. package org.meveo.util; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Properties; import javax.naming.InitialContext;...

- How To Integrate Apache Shiro With Javaee6
This tutorial will show the readers, how I was able to integrate Apache Shiro with JavaEE6. My technology Stack: JavaEE6, Glassfish3.1.2.2, Apache Shiro1.2 Steps: 1.) Create a maven project and include the following dependency. <dependencies> <dependency>...

- How To Load Property File From Glassfish Config's Folder
In seam you can define a component class in components.xml that will load the properties from the JBOSS_CONFIG folder. Add the ff lines: <component name="paramBean" class="com.ipil.PropertyBean" scope="application" auto-create="true" startup="true">...



Tech-Today








.