no

Getting started with jax-rs security

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 ...

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

References:

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

Related

javaee-rest 6179656862870527965

Post a Comment Default Comments

item