One of my tasks for the current iteration was to add security constraints to the J2EE web service that we are currently developing. This is the easy part. Simply define the appropriate security-constraint, login-config, and security-role elements in the project's web.xml.
web.xml
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"> <display-name>...</display-name> <servlet>...</servlet> <servlet-mapping>...</servlet-mapping> <security-constraint> <display-name>deny unauthorized users</display-name> <web-resource-collection> <web-resource-name>global</web-resource-name> <url-pattern>/</url-pattern> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>AUTHORIZED_USER</role-name> </auth-constraint> <user-data-constraint> <!-- All access to this area will be SSL protected --> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>APP</realm-name> </login-config> <security-role> <description>authorized user for application</description> <role-name>AUTHORIZED_USER</role-name> </security-role> </web-app>
From the above, you can see that I defined an expected role, AUTHORIZED_USER, an expected realm for http basic authentication, APP, and a set of resources, / and /*, which can only be accessed through SSL by a user who is a member of the AUTHORIZED_USER role. This is the easy part and should work for most application servers which are worth their salt.
Enter jetty-maven-plugin
This project is already using the jetty-maven-plugin to run a test instance of the application. I thought it would be a good idea to make sure that the security on the localhost instance for testing would work in the same manner as the WebSphere server to which the application is to be deployed. This would help me to ensure that I, as a lazy programmer, would not have to change the functional tests (SoapUI) between localhost and dev.
By default, the jetty instance created by executing mvn jetty:run-war does not include a user realm for defining users and groups, a login service for handling attempted logins, or even the capability for handling SSL connections. In order to bring these components into the localhost instance, I had to make some changes to the configuration of my project's jetty-maven-plugin. First, the changes for enabling the user realm and login service.
pom.xml (version 1)
<project xmlns="http://maven.apache.org/POM/4.0.0" ... <build> <plugins> ... <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>7.3.0.v20110203</version> <configuration> ... <userRealms> <userRealm implementation="org.mortbay.jetty.security.HashUserRealm"> <name>APP</name> <config>${project.build.directory}/test-classes/jetty-users.properties</config> </userRealm> </userRealms> <loginServices> <loginService implementation="org.eclipse.jetty.security.HashLoginService"> <name>APP</name> <config>${project.build.directory}/test-classes/jetty-users.properties</config> </loginService> </loginServices> </configuration> </plugin> ... </plugins> ... </build> ... </project>
/src/test/resources/jetty-users.properties
test_user=test_user,AUTHORIZED_USER
Using the keytool-maven-plugin for generating a certificate for SSL
In order to enable server authentication, the Jetty instance needs to have access to a server certificate to be sent out in the SSL handshake. I did some investigation and found that there was a plugin, keytool-maven-plugin, which would allow you to automate self-signed certificate generation in the maven execution. I modified the project's pom as follows:>
pom.xml (version 2)
<project xmlns="http://maven.apache.org/POM/4.0.0" ... <build> <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>keytool-maven-plugin</artifactId> <executions> <execution> <phase>generate-resources</phase> <id>clean</id> <goals> <goal>clean</goal> </goals> </execution> <execution> <phase>generate-resources</phase> <id>genkey</id> <goals> <goal>genkey</goal> </goals> </execution> </executions> <configuration> <keystore>${project.build.directory}/jetty-ssl.keystore</keystore> <dname>cn=active-active.blogspot.com</dname> <keypass>jetty6</keypass> <storepass>jetty6</storepass> <alias>jetty6</alias> <keyalg>RSA</keyalg> </configuration> </plugin> ... </plugins> ... </build> ... </project>
Adding SSL support to Jetty
Now, we have a way for the client to authenticate itself, a realm for assigning that client's roles, and a keystore. We just need to tell Jetty how to expose an SSL port to the world.
pom.xml (version 3)
<project xmlns="http://maven.apache.org/POM/4.0.0" ... <build> <plugins> ... <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>7.3.0.v20110203</version> <configuration> ... </userRealms> <connectors> <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector"> <port>8080</port> <maxIdleTime>60000</maxIdleTime> </connector> <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector"> <port>8443</port> <maxIdleTime>60000</maxIdleTime> <keystore>${project.build.directory}/jetty-ssl.keystore</keystore> <password>jetty6</password> <keyPassword>jetty6</keyPassword> </connector> </connectors> <loginServices> ... </configuration> </plugin> ... </plugins> ... </build> ... </project>
A big thank-you to mrhaki and his article Configure Maven Jetty Plugin for SSL Communication for the assist.
Comments