Introduction

In this blog post I will provide a brief introduction to JASPIC and then take a walk through setting up a basic demo using JASPIC to secure a simple web application in GlassFish.

What is JASPIC?

JASPIC stands for Java Authentication Service Provider Interface for Containers. The original JSR for JASPIC was created back in 2002 but it wasn't completed until 2007 and wasn't included in Java EE until Java EE 6 back in 2009.

JSR 196 defines a standard service-provider interface (SPI) and standardises how an authentication module is integrated into a Java EE container. It is supported by all the popular web containers and is mandatory for the full Java EE 6 profile. It provides a message processing model and details a number of interaction points on the client and server.

A compatible web container will use the SPI at these points to delegate the corresponding message security processing to a server authentication module (SAM).

Walk-through

I will be using the following software whilst doing this walk-through. If you are using different versions then you may see different results.

Ubuntu 14.04 LTS

Eclipse Luna (with Glassfish Tools - available here - http://marketplace.eclipse.org/content/glassfish-tools-luna

JDK 1.7.0_25

GlassFish 4.1

Creating the Runtime Environment

First of all, we will create a runtime environment so we can run GlassFish from within Eclipse. In order to do so:

In Eclipse go to: File->New->Other->Server

Select Glassfish 4

Set the JDK

Set the Server directory to <GF_INSTALL_DIR>/glassfish

All other defaults should be OK.

Creating the web application

OK. So, next we will create a very basic web-app consisting of a single servlet.

In Eclipse go to:

File -> New -> Dynamic Web Project

Name - JASPICTest

Make sure the Target runtime is GlassFish 4.

Press Next twice to accept the defaults and then select Generate web.xml deployment descriptor on the last page.

Click Finish.

Right-click on your newly created project and select New → Servlet:

package - uk.co.c2b2

Class Name - TestServlet

Click Next and Finish

This will create a basic servlet. Add the following code to the doGet method: try { out.println("<html>"); out.println("<head>"); out.println("<title>Test Servlet</title>"); out.println("</head>"); out.println("<body>"); out.println("You have accessed TestServlet at " + request.getContextPath () + "<br>"); Principal userPrincipal = request.getUserPrincipal(); boolean adminUser = request.isUserInRole("admin"); String userName; if (userPrincipal != null) { userName = userPrincipal.getName(); } else { userName = "Unknown User"; } out.println("You are currently authenticated as: " + userName + "<br>"); if (adminUser) { out.println("<br>As you're admin you can view this.<br>"); } else { out.println("<br>Sorry, you're not admin. Nothing to see here.<br>"); } out.println("</body>"); out.println("</html>"); } finally { out.close(); }

This is very basic but will allow us to see the relevant authentication data being returned by the server.

Testing your application

Start your GlassFish server - Right click on your server and click Start. (Note - If you can't see the Servers tab ensure you have the Java EE perspective open).

To run your servlet right click on TestServlet, select Run As -> Run on Server.

Make sure your GlassFish server is selected and hit Finish. Click on Always use this server when running this project to make things simpler in future.

Go to: http://localhost:8080/JASPICTest/TestServlet and you should get the following response:

You have accessed TestServlet at /JASPICTest2 You are currently authenticated as: Unknown User

Sorry, you're not admin. Nothing to see here.

Creating the Server Authentication Module

The Server Authentication Module (SAM) must implement the javax.security.auth.message.module.ServerAuthModule interface as defined by JSR 196. The interface can be found here:

The SAM is invoked indirectly by the message processing runtime at the validateRequest and secureResponse interaction points.

Create a new Java project - TestAuthModule. Right click project, select Build Path-> Configure Build Path->Libraries. Click Add External JARs.

Add the following dependencies:

javax.servlet-api.jar

javax.security.auth.message-api.jar

Both can be found in:

<GF_INSTALL_DIR>/glassfish/modules

These will only be used to compile the code, you don't need to package them up as the web container will already contain copies.

Right click on your new project and create a new Java class TestAuthModule which implements the interface - javax.security.auth.message.module.ServerAuthModule

For now we won't add in anything aside from replacing the auto-generated validateRequest method with the following: public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { System.out.println("validateRequest called"); return AuthStatus.SUCCESS; }

Export your new project as a jar file. Right click on the TestAuthModel project, select Export->Java->JarFile. Remove the classpath and project resources and call the output file TestAuthModule.jar. Export the jar file to <GF_INSTALL_DIR>/glassfish/lib. Restart Glassfish.



Configuring the SAM



Next up we need to create a message security provider in GlassFish and then link this to our web app.

Go to the GlassFish admin console:

http://localhost:4848



Go to Configurations->server-config->Security->Message Security

Select HttpServlet

Select the Providers tab. Click New.

Provider ID - TestSAM

Provider Type - server

Class Name - uk.co.c2b2.TestAuthModule

Updating the web app



In the JASPICTest app, edit glassfish-web.xml. Add the following to indicate that the TestSAM you have just set up should be used for this app.

<glassfish-web-app httpservlet-security-provider="TestSAM">

Testing the changes

In Eclipse - Right click on TestServlet and select Run As -> Run On Server

You should now see in the Glassfish console output:

INFO: validateRequest called

This shows that Glassfish our SAM is now being used.

Locking down resources



OK, so at the moment we have a web app that is linked to our SAM but we haven't actually said to secure anything and even if we did our SAM simply authenticates anyone!



So, lets implement some (albeit very basic) security.



NOTE - This is only for demo purposes and to show how JASPIC works, it is most definitely not intended to be a way of doing security!.



First of all, let's lock down our servlet. We want to lock it down to only users with the role admin or standard. To do so, add the following to the application web.xml:

<security-constraint> <web-resource-collection> <web-resource-name>JASPICTest</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>standard</role-name> <role-name>admin</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>standard</role-name> </security-role> <security-role> <role-name>admin</role-name> </security-role>

There is one additional (rather ugly) step we need to do to make our app work. In order for GlassFish to accept the roles that our authentication module puts into the JAAS Subject we have to map them to groups.

In order to do so, add the following to glassfish-web.xml

<security-role-mapping> <role-name>standard</role-name> <group-name>standard</group-name> </security-role-mapping> <security-role-mapping> <role-name>admin</role-name> <group-name>admin</group-name> </security-role-mapping>

Next up we will alter our SAM and implement the methods. Each of the methods is implemented as follows:



initialize - Simply takes the CallBackHandler and instantiates our local handler.

getSupportedMessageTypes - Returns the HTTP servlet request and response types.

secureResponse - Simply returns Success.

cleanSubject - Clears all principals from the Subject.



validateRequest - This is the main method of interest. In order to pass in the user and role I have just added them as servlet request parameters for testing purposes. This method extracts those values and then calls authenticateUser.



authenticateUser - NOTE - This method doesn't actually do any authentication! It simply takes the user and group, creates callback classes from them and passes them to the callback handler.



Once we have added the above our TestAuthModule looks like this:

public class TestAuthModule implements ServerAuthModule { @SuppressWarnings("rawtypes") protected static final Class[] supportedMessageTypes = new Class[] { HttpServletRequest.class, HttpServletResponse.class }; private CallbackHandler handler; public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, @SuppressWarnings("rawtypes") Map options) throws AuthException { System.out.println("initialize called."); this.handler = handler; } @SuppressWarnings("rawtypes") public Class[] getSupportedMessageTypes() { return supportedMessageTypes; } public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serverSubject) throws AuthException { HttpServletRequest request = (HttpServletRequest)messageInfo.getRequestMessage(); String user = request.getParameter("user"); String group = request.getParameter("group"); System.out.println("validateRequest called."); System.out.println("User = " + user); System.out.println("Group = " + group); authenticateUser(user, group, clientSubject, serverSubject); return AuthStatus.SUCCESS; } public AuthStatus secureResponse(MessageInfo msgInfo, Subject service) throws AuthException { System.out.println("secureResponse called."); return AuthStatus.SEND_SUCCESS; } public void cleanSubject(MessageInfo msgInfo, Subject subject) throws AuthException { if (subject != null) { subject.getPrincipals().clear(); } } private void authenticateUser(String user, String group, Subject clientSubject, Subject serverSubject) { System.out.println("Authenticating user " + user + " in group " + group); CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, user); GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(clientSubject, new String[] { group }); try { handler.handle(new Callback[] { callerPrincipalCallback, groupPrincipalCallback }); } catch (Exception e) { e.printStackTrace(); } } }

Testing



Now all we need to do is test our new module. First of all build it, jar it up and copy it over to <GF_INSTALL_DIR>/glassfish/lib as before. Restart Glassfish. Now we can test by passing in different dummy credentials. You should see the following:





If you go to:

http://localhost:8080/JASPICTest/TestServlet?user=Andy&group=standard

You should see the "Sorry, you're not admin. Nothing to see here." message.



If you go to:



http://localhost:8080/JASPICTest/TestServlet?user=Andy&group=admin



You should see the "As you're admin you can view this." message.



And if you go to:



http://localhost:8080/JASPICTest/TestServlet?user=Andy&group=xxx



You should see a HTTP Status 403 - Forbidden message.



Wrapping Up



Hopefully this has given you a taster of how to use JASPIC to secure your applications and you can see how relatively straightforward it is to put the basic building blocks in place. If you're looking for an example of a SAM that does authentication then there is one available from Oracle here:



http://docs.oracle.com/cd/E19798-01/821-1752/gizeb/index.html



Although JASPIC is yet to take off it's a good first step towards standardising security in web containers and avoids the need for each to have their own proprietary solution, although there is still the issue of different containers using different deployment descriptors hindering the portability of apps.