CARVIEW |
![]() Get Involved
Get Informed
Get Connected
Search
Online Books:
|
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
Marc Hadley's Blog
Authentication in JerseyPosted by mhadley on March 07, 2008 at 01:14 PM | Permalink | Comments (0)I'm working on an internal project building some RESTful services using a combination of Jersey, JPA, Glassfish and Derby. Actions on some resources require authentication and I need access to the name of the authenticated user in the resource method. This entry describes the steps to set this up - kudos to my colleague Hubert for working out much of the below. Obviously you're going to need all the ingredients listed above. I'm using NetBeans which came bundled with Glassfish and Derby so all I had to do was install the RESTful Web Services plug-in, YMMV. Next you need a database to store username, password and group information. Here's the table definitions I used: CREATE TABLE users ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, username VARCHAR(64) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL, ); CREATE INDEX username ON users(username); CREATE TABLE groups ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, username VARCHAR(64) NOT NULL REFERENCES users(username) ON DELETE CASCADE, groupname VARCHAR(64) ); Depending on your needs you could lose the integer fields, the important thing is to have a column in the group table with the same name as the column in the users table that holds the username. Those two columns are used to join the users table to the groups table. The Glassfish security realm is going to execute a query like this to retrieve the groups (which map to security roles) that a particular user is a member of: SELECT groupname FROM groups g, users u where g.username = u.username and u.username = ? Add some users and groups for testing purposes, note that the password should be an md5 hash, not plaintext - there are several web sites that offer online MD5 generators that you can use. I used groups named USERS and ADMINISTRATORS, you'll see where these come in later. Next you need to define a JDBC data source, connection pool and security realm in Glassfish. The first two are pretty straightforward and will likely be taken care of already if you have deployed a web application using the database. Setting up the security realm is also straightforward once you have a suitable database structure as described above. You do this via the admin console as shown below (there are a couple of additional fields on the form, you can leave those blank): ![]() Its now possible to require authentication for access to a JAX-RS resource via web.xml. Say I have a resource that only authenticated users can use, e.g.: @Path("dropbox") public class DropBox { @Context SecurityContext security; @POST public Response drop(InputStream data) { String username = security.getUserPrincipal().getName(); ... } } Note the use of dependency injection to get an instance of <security-constraint> <display-name>DropBox</display-name> <web-resource-collection> <web-resource-name>DropBox</web-resource-name> <description></description> <url-pattern>/dropbox</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> <http-method>HEAD</http-method> <http-method>PUT</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> <http-method>DELETE</http-method> </web-resource-collection> <auth-constraint> <description>Have to be a USER</description> <role-name>USERS</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>userauthn</realm-name> </login-config> <security-role> <description/> <role-name>USERS</role-name> </security-role> That about it, after you redeploy the application, the next time you try to access Integrating Jersey and AbderaPosted by mhadley on February 05, 2008 at 11:38 AM | Permalink | Comments (4)I'm working on an internal project that involves adding Atom Publishing Protocol support to a data store. Naturally, I'm using Jersey for the HTTP side of things and decided to give Apache Abdera a try for simplifying working with feeds and entries. With JAX-RS I can write a feed resource pretty easily: import java.net.URI; import java.util.Date; import javax.ws.rs.ConsumeMime; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.ProduceMime; import javax.ws.rs.UriParam; import javax.ws.rs.core.HttpContext; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.apache.abdera.model.Entry; import org.apache.abdera.model.Feed; @ProduceMime("application/atom+xml") @ConsumeMime("application/atom+xml") @Path("myfeed") public class FeedResource { @HttpContext private UriInfo uriInfo; @GET public Feed getFeed() { Feed f = AbderaSupport.getAbdera().getFactory().newFeed(); f.setTitle("..."); f.setId(...); f.addAuthor(...); f.setUpdated(...); URI feedLink = uriInfo.getRequestUri(); f.addLink(feedLink.toString(),"self"); for (...) { Entry e = f.addEntry(); URI entryLink = ... f.addLink(entryLink.toString(),"alternate"); ... } return f; } @POST public Response addEntry(Entry e) { Entry newEntry = AbderaSupport.getAbdera().newEntry(); URI entryLink = ...; ... return Response.created(entryLink).entity(newEntry).build(); } } The To allow use of Abdera's import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.ws.rs.ProduceMime; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import org.apache.abdera.Abdera; import org.apache.abdera.model.Document; import org.apache.abdera.model.Element; import org.apache.abdera.model.Entry; import org.apache.abdera.model.Feed; @Provider @ProduceMime("application/atom+xml") @ConsumeMime("application/atom+xml") public class AbderaSupport implements MessageBodyWriter<Object>, MessageBodyReader<Object> { private final static Abdera abdera = new Abdera(); public static Abdera getAbdera() { return abdera; } public long getSize(Object arg0) { return -1; } public boolean isWriteable(Class<?> type) { return (Feed.class.isAssignableFrom(type) || Entry.class.isAssignableFrom(type)); } public void writeTo(Object feedOrEntry, MediaType mediaType, MultivaluedMap<String, Object> headers, OutputStream outputStream) throws IOException { if (feedOrEntry instanceof Feed) { Feed feed = (Feed)feedOrEntry; Document<Feed> doc = feed.getDocument(); doc.writeTo(outputStream); } else { Entry entry = (Entry)feedOrEntry; Document<Entry> doc = entry.getDocument(); doc.writeTo(outputStream); } } public boolean isReadable(Class<?> type) { return (Feed.class.isAssignableFrom(type) || Entry.class.isAssignableFrom(type)); } public Object readFrom(Class<Object> feedOrEntry, MediaType mediaType, MultivaluedMap<String, String> headers, InputStream inputStream) throws IOException { Document<Element> doc = getAbdera().getParser().parse(inputStream); Element el = doc.getRoot(); if (feedOrEntry.isAssignableFrom(el.getClass())) { return el; } else { throw new IOException("Unexpected payload, expected "+feedOrEntry.getName()+ ", received "+el.getClass().getName()); } } } Notice I actually implemented Abdera is shaping up to be a nice API for working with Atom documents. Hopefully the above demonstrates how easy it is to integrate support for new formats into Jersey. ![]() |
![]() |
March 2008
Search this blog:CategoriesCommunity: Java Web Services and XMLCommunity: Mac Java Community Distributed J2EE J2SE Programming Web Services and XML Archives
March 2008 Recent Entries![]() |
![]()
|