In the
Security REST service using web.xml entry i talked about how you can protect a REST service by adding security-constraint element in the web.xml. But JAXRS provides annotations that would give you more granular control over the REST services. I wanted to try that feature so i changed the same ManageContactApp to use the
javax.annotation.security annotations. You can download the sample application from
here
When the user tries to insert a new record he will get prompted for basic authentication like this

I followed these steps to build the sample application
- First i did download the basic ManageContactApp.zip that provides REST interface and i tested it to make sure that it works
- Next i used the instructions in Securing web application deployed in Jetty to make changes in Maven build file(pom.xml) to enable loginService in Jetty
<build>
<finalName>JettySecurity</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.4.5.v20110725</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webAppConfig>
<contextPath>/JettySecurity</contextPath>
</webAppConfig>
<loginServices>
<loginService implementation="org.eclipse.jetty.security.HashLoginService">
<name>Default</name>
<config>${basedir}/src/main/resources/realm.properties</config>
</loginService>
</loginServices>
<connectors>
<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
<port>9000</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
</plugin>
</plugins>
</build>
- Next create realm.properties file in
${basedir}/src/main/resources directory which looks like this
guest:guest
admin:admin,ADMIN
This file has only 2 users first is guest and second is admin the admin user has ADMIN role.
- Next change the web.xml file to declare the ADMIN role and define BASIC authentication scheme for the web application like this.
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.webspherenotes.rest.ContactApplication</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>
com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Default</realm-name>
</login-config>
<security-role>
<role-name>ADMIN</role-name>
</security-role>
</web-app>
By default the Jersey implementation does not look for security annotations in your REST service, in order for that to work you must set value of com.sun.jersey.spi.container.ResourceFilters init parameter to com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory this filter takes care of parsing and understanding PermitAll, RolesAllowed and DenyAll annotations
- The last step is to change the ContactService.java to use the security related annotations at individual class and method level
package com.webspherenotes.rest;
import java.util.List;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("/contact")
public class ContactService {
Logger logger = LoggerFactory.getLogger(ContactService.class);
EntityManagerFactory entityManagerFactory;
public ContactService(EntityManagerFactory entityManagerFactory){
this.entityManagerFactory=entityManagerFactory;
}
@GET
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public List getContactList() {
logger.debug("Entering ContactService.getContactList()");
EntityManager entityManager = entityManagerFactory.createEntityManager();
Query q = entityManager.createQuery("SELECT x from Contact x");
logger.debug("Exiting ContactService.getContactList()");
return (List) q.getResultList();
}
@GET
@Path("/{contactId}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Contact getContact(@PathParam("contactId") int contactId) {
logger.debug("Entering ContactService.getContact() contactId" + contactId);
EntityManager entityManager = entityManagerFactory.createEntityManager();
Contact contact = entityManager.find(Contact.class, contactId);
logger.debug("Exiting ContactService.getContact()" );
return contact;
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@RolesAllowed("ADMIN")
public Contact insertContact(@FormParam("contactId") int contactId,
@FormParam("firstName") String firstName,
@FormParam("lastName") String lastName,
@FormParam("email") String email) {
logger.debug("Entering ContactService.insertContact()");
EntityManager entityManager = entityManagerFactory.createEntityManager();
Contact contact = new Contact();
contact.setContactId(contactId);
contact.setFirstName(firstName);
contact.setLastName(lastName);
contact.setEmail(email);
try{
entityManager.getTransaction().begin();
entityManager.persist(contact);
entityManager.getTransaction().commit();
}catch(Throwable t){
if(entityManager.getTransaction().isActive())
entityManager.getTransaction().rollback();
contact = null;
}finally{
entityManager.close();
}
logger.debug("Exiting ContactService.insertContact()");
return contact;
}
@PUT
@Path("/{contactId}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@RolesAllowed("ADMIN")
public Contact updateContact(@PathParam("contactId") int contactId,
@FormParam("firstName") String firstName,
@FormParam("lastName") String lastName,
@FormParam("email") String email) {
logger.debug("Entering ContactService.update() contactId" + contactId);
EntityManager entityManager = entityManagerFactory.createEntityManager();
Contact contact = new Contact();
contact.setContactId(contactId);
contact.setFirstName(firstName);
contact.setLastName(lastName);
contact.setEmail(email);
try{
entityManager.getTransaction().begin();
entityManager.merge(contact);
entityManager.getTransaction().commit();
}catch(Throwable t){
if(entityManager.getTransaction().isActive())
entityManager.getTransaction().rollback();
contact = null;
}finally{
entityManager.close();
}
logger.debug("Exiting ContactService.updateContact()");
return contact;
}
@DELETE
@Path("/{contactId}")
@RolesAllowed("ADMIN")
public void deleteContact(@PathParam("contactId") int contactId) {
logger.debug("Entering ContactService.deleteContact() contactId " + contactId);
EntityManager entityManager = entityManagerFactory.createEntityManager();
try{
entityManager.getTransaction().begin();
Contact contact = entityManager.find(Contact.class, contactId);
logger.debug("remove contact " + contact);
entityManager.remove(contact);
logger.debug("After removing " + contact);
entityManager.getTransaction().commit();
}catch(Throwable t){
if(entityManager.getTransaction().isActive())
entityManager.getTransaction().rollback();
}finally{
entityManager.close();
}
logger.debug("Exiting ContactService.deleteContact()");
}
}
By default all the methods are accessible to user. But i did add @RolesAllowed("ADMIN") to insertContact(), updateContact() and deleteContact() method to say that only users who have admin rights can access these methods.
No comments:
Post a Comment