Java EE: Setting up and Testing Form-Based JDBC Authentication with Arquillian and Maven

December 21st, 2014 by

Especially when it comes to testing, setting up a decent environment for a secured Java EE web application isn’t always an easy thing to do.

In the following tutorial I’d like to demonstrate how to create a secured web application using form-based authentication and a JDBC realm to fetch users and roles and how to run the application in an embedded container for testing and development.

Additionally I’d like to show how to write and run integration tests to verify the security setup using a setup of Maven, Embedded GlassFish, Arquillian, jUnit and rest-assured.

Testing the RESTful webservice with Arquillian and rest-assured

Testing the RESTful webservice with Arquillian and rest-assured

 

About / TLDR;

In the following tutorial we’re going to set up step-by-step a web application consisting of the following elements:

  • A RESTful web-service that allows a user to log-out and to retrieve user information. We’ll be applying restrictions to the user information API programmatically.
  • A simple secured website. Restrictions are applied using XML declaration.
  • Login and login-error pages for the form-based authentication so that a user is able to login.
  • A dummy bootstrap class that adds a sample user with user-groups to the database

The goal behind the complex setup described in the following chapters is to finally have a mavenized project that allows us to..

  • Initialize and start-up a full configured Java EE application container with everything we need already set up e.g. security configuration, JDBC connection pool, an embedded H2 database running, user and group-mappings already configured, persistence working etc..
  • Run full integration tests with Arquillian by automatically configure and start-up an embedded GlassFish container matching the same criteria as above.

Creating the Application

In the following chapter, we’re creating a new project and we’re implementing a simple Java EE web application with some basic HTML pages and a RESTful web-service.

If you’re mainly interesting in the security configuration, please feel free to skip directly to the chapter “Applying Security Configuration“.

Project Setup / Dependencies

Using Maven, to set up a new Java EE web project is quite simple using one of the specific existing archetypes (I’m using org.codehaus.mojo.archetypes:webapp-javaee6:1.5 for this tutorial).

New Java EE Webproject using Maven Archetypes

New Java EE Webproject using Maven Archetypes

In addition to the dependencies added by the archetype, we need the following libraries to setup our application:

  • Arquillian Stack: arquillian-bom, arquillian-glassfish-embedded, arquillian-junit-container
  • Persistence Provider: eclipselink (or hibernate, openJPA etc..)
  • In-memory-database: h2
  • Embedded GlassFish: glassfish-embedded-all, maven-embedded-glassfish-plugin
  • Test-Framework-Stack: junit, hamcrest-all, rest-assured

This is an excerpt from my pom.xml:

<project>
	[..]
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.jboss.arquillian</groupId>
				<artifactId>arquillian-bom</artifactId>
				<version>1.1.5.Final</version>
				<scope>import</scope>
				<type>pom</type>
			</dependency>
		</dependencies>
	</dependencyManagement>
 
	<dependencies>
		<dependency>
			<groupId>org.glassfish.main.extras</groupId>
			<artifactId>glassfish-embedded-all</artifactId>
			<version>3.1.2.2</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.arquillian.container</groupId>
			<artifactId>arquillian-glassfish-embedded-3.1</artifactId>
			<version>1.0.0.CR4</version>
			<scope>test</scope>
		</dependency>
		[..]
		<dependency>
			<groupId>org.jboss.arquillian.junit</groupId>
			<artifactId>arquillian-junit-container</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.jayway.restassured</groupId>
			<artifactId>rest-assured</artifactId>
			<version>2.4.0</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>eclipselink</artifactId>
			<version>2.6.0-M3</version>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>1.4.182</version>
		</dependency>
	</dependencies>
 
	<build>
		<plugins>
			[..]
			<plugin>
				<groupId>org.glassfish</groupId>
				<artifactId>maven-embedded-glassfish-plugin</artifactId>
				<version>3.1.1</version>
				[..]
			</plugin>
		</plugins>
	</build>
</project>

The complete pom.xml is available here in my repository.

Embedded GlassFish Maven Plugin

This is the configuration that allows us to start a complete GlassFish server instance with our application deployed and customized regarding the configuration of our JDBC connections, connection pools, resources and security configuration:

<plugin>
	<groupId>org.glassfish</groupId>
	<artifactId>maven-embedded-glassfish-plugin</artifactId>
	<version>3.1.1</version>
	<configuration>
		<goalPrefix>glassfish</goalPrefix>
		<app>${project.build.directory}/${project.build.finalName}</app>
		<port>8080</port>
		<contextRoot>${project.name}</contextRoot>
		<name>${project.name}</name>
		<autoDelete>true</autoDelete>
		<configFile>${project.build.outputDirectory}/config/domain.xml</configFile>
		<systemProperties>
			<property>glassfish.embedded.tmpdir=target/glassfish</property>
		</systemProperties>
	</configuration>
	<executions>
		<execution>
			<id>install</id>
			<phase>install</phase>
			<goals>
				<goal>run</goal>
			</goals>
		</execution>
		<execution>
			<id>deploy</id>
			<phase>deploy</phase>
			<goals>
				<goal>war:exploded</goal>
			</goals>
		</execution>
	</executions>
</plugin>

This configuration finally allows us to start the embedded GlassFish and access our web-application at http://localhost:8080/hascode.

CDI Configuration

This part is only relevant when the target platform is a Java EE <=6 application container.

To make the CDI discovery work, we simply need to create an empty file named beans.xml in src/main/webapp/WEB-INF

Entities

This is the minimal set of entities used. A user has a userid and a password (which we’re hiding when serializing a user in our REST services using @XmlTransient) and belongs to groups.

This is our user entity

package com.hascode.tutorial.entity;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
 
@Entity
@Table(name = "users")
@XmlRootElement
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class User {
	@Id
	@Column(name = "userid")
	private String userid;
 
	@Column(name = "password")
	private String password;
 
	@ElementCollection
	@CollectionTable(name = "users_groups", joinColumns = @JoinColumn(name = "userid"))
	private List<UserGroup> groups = new ArrayList<UserGroup>();
 
	@XmlTransient
	public String getPassword() {
		return password;
	}
 
	// other getters, setters ommitted
 
}

And this is the entity for user roles, nothing special here..

package com.hascode.tutorial.entity;
 
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlRootElement;
 
@Embeddable
@XmlRootElement
public class UserGroup {
 
	@Column(name = "groupid")
	private String groupid;
 
	// ..
}

JPA Persistence Configuration

Now we need to configure our JPA persistence by adding the following persistence.xml to our src/main/resoures/META-INF directory.

In this file we’re adding a persistence unit with container-managed-persistence (transaction-type=”JTA”), EclipseLink as persistence provider and a JNDI reference to the data-source that we’ll be adding to the container configuration in a few steps.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
	<persistence-unit name="default" transaction-type="JTA">
		<provider>oracle.toplink.essentials.PersistenceProvider</provider>
		<jta-data-source>jdbc/hascode_test_db</jta-data-source>
	</persistence-unit>
</persistence>

EclipseLink scans for entities automatically so we don’t need to add class/entity references to the file.

Secured RESTful Web-Service for Session Management

To access our REST-services at a prefixed path of “rs“, we’re adding the following configuration class to our application:

package com.hascode.tutorial.ws;
 
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 
@ApplicationPath("/rs")
public class RestConfiguration extends Application {
}

Our RESTful web-service exports two methods: The first allows a user to log-out and to invalidate his session, the second one returns user information for the current user doing a JPA look-up in the database.

Only members of the user-group administrators are allowed to access our REST service -  we’re granting this permission by adding the annotation @RolesAllowed({“administrators”}) at class level.

This is the full implementation of our web-service as a stateless session bean.

package com.hascode.tutorial.ws;
 
import java.security.Principal;
 
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
 
import com.hascode.tutorial.entity.User;
 
@Stateless
@Path("/session")
@RolesAllowed(value = { "administrators" })
public class AuthenticationWebservice {
	@PersistenceContext
	private EntityManager em;
 
	@Path("/logout")
	@POST
	public Response logout(@Context final SecurityContext ctx, @Context final HttpServletRequest request) {
		request.getSession().invalidate();
		return Response.ok().build();
	}
 
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public Response getCurrentUserInformation(@Context final SecurityContext ctx) {
		Principal principal = ctx.getUserPrincipal();
 
		User user = em.find(User.class, principal.getName());
		return Response.ok(user).build();
	}
}

Securing a simple website

To demonstrate how a simple website may be secured, we need to add  this sample  website to our application.

Later on, this website should not be visible for unauthorized users:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>secured area</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
	</head>
	<body>
		<h1>Secured area</h1>
		<a href="/hascode/rs/session">Session Information</a>
	</body>
</html>

Applying Security Configuration

Now we’re ready to add some users and groups to our application and add security information where needed.

Setting up Users and Roles

To test our application, we need users and user groups. The following snippet is a quick and ugly way, to create the necessary database structure and to add a user with userid = admin who belongs to the usergroup = administrators.

This is for demonstration purpose, thought and in a real project you should definitely make use of a database migration framework.

In case you’re interested in this topic, I’d like to recommend two articles of mine: “Java EE 7 Database Migrations with Liquibase and WildFly” and “Easy Database Migrations using Flyway, Java EE 6 and GlassFish“.

As we will see later when adding the container configuration, we’re using the following database structure to map users, groups and user to group assignments:

User and Groups Database Structure

User and Groups Database Structure

This is our bootstrap loader, implemented as a singleton EJB that is run on application start-up (achieved by adding @Startup):

package com.hascode.tutorial.control;
 
import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Logger;
 
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.sql.DataSource;
 
@Singleton
@Startup
public class ApplicationConfigurationBean {
	private final Logger log = Logger.getLogger(ApplicationConfigurationBean.class.getName());
 
	@Resource(lookup = "jdbc/hascode_test_db")
	private DataSource ds;
 
	@PostConstruct
	protected void onStartup() throws SQLException {
		log.info("initializing users and roles..");
		// use a migration framework here - this is just for the purpose of
		// demonstration
		String createUserTable = "CREATE TABLE `users` (`userid` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`userid`))";
		String createGroupTable = "CREATE TABLE `users_groups` ( `groupid` varchar(20) NOT NULL, `userid` varchar(255) NOT NULL)";
		String addAdminUser = "INSERT INTO `users` VALUES('admin', 'test')";
		String addUserToAdminGroup = "INSERT INTO users_groups VALUES('administrators','admin')";
 
		Connection con = ds.getConnection();
 
		con.prepareCall(createUserTable).execute();
		con.prepareCall(createGroupTable).execute();
		con.prepareCall(addAdminUser).execute();
		con.prepareCall(addUserToAdminGroup).execute();
		log.info("user and roles setup completed");
	}
}

Alternatively we may just run the following SQL snippet directly on our database:

CREATE TABLE `users` (`userid` VARCHAR(255) NOT NULL, `password` VARCHAR(255) NOT NULL, PRIMARY KEY (`userid`));
CREATE TABLE `users_groups` ( `groupid` VARCHAR(20) NOT NULL, `userid` VARCHAR(255) NOT NULL, FOREIGN KEY (userid) REFERENCES users(userid));
 
INSERT INTO `users` VALUES('admin', 'test');
INSERT INTO users_groups VALUES('administrators','admin');

Mapping Server Roles to Application Groups

Complex as Java EE sometimes still is, we need to map user roles specified for the application server to user groups specified in our concrete application.

This is done by adding the following glassfish-web.xml to the src/main/webapp/WEB-INF directory.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
	<security-role-mapping>
		<role-name>administrators</role-name>
		<group-name>administrators</group-name>
	</security-role-mapping>
	<class-loader delegate="true" />
</glassfish-web-app>

The older version of this mapping file, named sun-web.xml does also work, you simply need to replace the element glassfish-web-app with sun-web-app here.

Adding a Login Mask

We need a simple login mask to authenticate a user of our application.

As the path for security checks also as the parameters for user-name and password are specified by the security container, we simply need to obey these rules and use these conventions in our login form:

<body>
	<h1>Login</h1>
	<form method="post" action="j_security_check">
		<label>Username: <input type="text" name="j_username"/></label>
		<br/>
		<label>Password:<input type="password" name="j_password"/></label>
		<br/>
		<input type="submit" value="login" />
	</form>
</body>

Should a user fail to login, we’re showing this simple page with a link to the original login form.

<body>
	<h1>Login Error</h1>
	<a href="login.html">try again</a>
</body>

Adding Constraints and Role Requirements

By adding the following web.xml to our WEB-INF directory, we’re adding ..

  • ..  a new security role used by the application with the name administrators
  • ..  form based authentication to our application, bound to a realm named jdbcRealm (this is important when we’re modifying the container security later) and with a designated login and login-error page.
  • .. a security constraint for all files matching a pattern of *.html so that a user needs to fit the role administrators to access them.
<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <security-role>
    <description></description>
    <role-name>administrators</role-name>
  </security-role>
  <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>jdbcRealm</realm-name>
    <form-login-config>
      <form-login-page>/login.html</form-login-page>
      <form-error-page>/login_error.html</form-error-page>
    </form-login-config>
  </login-config>
  <security-constraint>
    <display-name>secured</display-name>
    <web-resource-collection>
      <web-resource-name>secured</web-resource-name>
      <url-pattern>*.html</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>administrators</role-name>
    </auth-constraint>
  </security-constraint>
</web-app>

Embedded GlassFish Configuration

Now to the fun part, tons of XML adding the configuration for our GlassFish application server.

To keep it simple, we’re  creating a new domain configuration using asadmin create-domain DOMAINNAME and use the generated domain.xml as a template for our configuration.

Afterwards we’re adding the domain.xml to our project structure (I have put mine in src/main/resources/config) so that we only need to modify the following parts to make everything work:

JDBC Connection Pool

We’re adding our in-memory H2 database to a new connection pool and make it accessible to the container using the JNDI handle jdbc/hascode_test_db.

<resources>
	<jdbc-connection-pool res-type="javax.sql.DataSource"
		datasource-classname="org.h2.jdbcx.JdbcDataSource"
		pool-resize-quantity="1" max-pool-size="5" steady-pool-size="1"
		statement-timeout-in-seconds="60" name="h2Pool">
		<property name="driverClass" value="org.h2.Driver" />
		<property name="URL" value="jdbc:h2:mem:hascode-h2-db;MVCC=TRUE;LOCK_TIMEOUT=10000;AUTO_RECONNECT=TRUE;DB_CLOSE_DELAY=-1"/>
		<property name="User" value="" />
		<property name="Password" value="" />
	</jdbc-connection-pool>
	<jdbc-resource object-type="system-all" enabled="true" jndi-name="jdbc/hascode_test_db" pool-name="h2Pool" />
</resources>
<servers>
	<server name="server" config-ref="server-config">
		[..]
		<resource-ref ref="jdbc/hascode_test_db"></resource-ref>
	</server>
</servers>
JDBC Realm

This is the setup for our JDBC realm to allow the container to lookup user and group assignments in a database.

NOTE: You should never, ever use digest-algorithm with NONE in production, give your passwords some hashing, dude ;)

In the following declaration, we’re also specifying where in the connected database users and groups are found.

<security-service default-realm="jdbcRealm" activate-default-principal-to-role-mapping="true">
	<auth-realm name="jdbcRealm"
		classname="com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm">
		<property name="digest-algorithm" value="NONE"></property>
		<property name="user-name-column" value="userid"></property>
		<property name="password-column" value="password"></property>
		<property name="group-name-column" value="groupid"></property>
		<property name="group-table" value="users_groups"></property>
		<property name="user-table" value="users"></property>
		<property name="datasource-jndi" value="jdbc/hascode_test_db"></property>
		<property name="jaas-context" value="jdbcRealm"></property>
		<property name="group-table-user-name-column" value="userid"></property>
	</auth-realm>
</security-service>

Application’s Directory Structure

By now, our project structure should look similar to this one (excepting the tests).

.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── hascode
    │   │           └── tutorial
    │   │               ├── control
    │   │               │   └── ApplicationConfigurationBean.java
    │   │               ├── entity
    │   │               │   ├── UserGroup.java
    │   │               │   └── User.java
    │   │               └── ws
    │   │                   ├── AuthenticationWebservice.java
    │   │                   └── RestConfiguration.java
    │   ├── resources
    │   │   ├── config
    │   │   │   └── domain.xml
    │   │   └── META-INF
    │   │       └── persistence.xml
    │   └── webapp
    │       ├── login_error.html
    │       ├── login.html
    │       ├── secured.html
    │       └── WEB-INF
    │           ├── beans.xml
    │           ├── glassfish-web.xml
    │           └── web.xml
    └── test
        ├── java
        │   └── it
        │       └── webservice
        │           └── AuthenticationWebserviceIT.java
        └── resources
            └── arquillian.xml

Running the Application on the fly

Maven and the embedded GlassFish plugin allows us to startup our application with one simple command:

$ mvn clean package org.glassfish:maven-embedded-glassfish-plugin:3.1.1:run
[..]
Dec 14, 2014 3:19:03 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: SEC1115: Realm [jdbcRealm] of classtype [com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm] successfully created.
[..]
INFO: Portable JNDI names for EJB ApplicationConfigurationBean : [java:global/hascode/ApplicationConfigurationBean!com.hascode.tutorial.control.ApplicationConfigurationBean, java:global/hascode/ApplicationConfigurationBean]
INFO: Portable JNDI names for EJB AuthenticationWebservice : [java:global/hascode/AuthenticationWebservice, java:global/hascode/AuthenticationWebservice!com.hascode.tutorial.ws.AuthenticationWebservice]
[..]
Dec 14, 2014 3:19:05 PM com.hascode.tutorial.control.ApplicationConfigurationBean onStartup
INFO: initializing users and roles..
Dec 14, 2014 3:19:06 PM com.hascode.tutorial.control.ApplicationConfigurationBean onStartup
INFO: user and roles setup completed
[..]
INFO: Registering the Jersey servlet application, named com.hascode.tutorial.ws.RestConfiguration, at the servlet mapping, /rs/*, with the Application class of the same name
Dec 14, 2014 3:19:06 PM com.sun.enterprise.web.WebApplication start
INFO: WEB0671: Loading application [hascode] at [/hascode]
[..]
INFO: hascode was successfully deployed in 3,949 milliseconds.
PlainTextActionReporterSUCCESSDescription: deploy AdminCommandApplication deployed with name hascode.
    [name=hascode
Dec 14, 2014 3:19:06 PM PluginUtil doDeploy
INFO: Deployed hascode
Hit ENTER to redeploy, X to exit

By now we should be able to access our application by opening http://localhost:8080/hascode in our browser.

Doing so, a login screen should immediatly appear – entering admin and test should take us to the secured website; clicking the link on the secured website calls the secured RESTful webservice to display user information.

Unstyled Login Screen

Unstyled Login Screen

Acessing a secured website

Acessing a secured website

Fetching user information from the RESTful webservice

Fetching user information from the RESTful webservice

Testing with Arquillian and rest-assured

Having a complete, running application now, we want to add some tests for our authentication process and the RESTful webservice now.

Arquillian Setup

The following configuration file named arquillian.xml tells Arquillian how to handle the container.

We put it in src/test/resources so that it doesn’t accidentally get copied when packaging a war file for production.

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://www.jboss.org/arquillian-1.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
      http://www.jboss.org/arquillian-1.0
      http://www.jboss.org/schema/arquillian/arquillian-1.0.xsd">
	<container qualifier="glassfish" default="true">
		<configuration>
			<property name="configurationXml">target/classes/config/domain.xml</property>
			<property name="bindHttpPort">8080</property>
		</configuration>
	</container>
	<engine>
		<property name="deploymentExportPath">target</property>
	</engine>
</arquillian>

Testing the REST-Service

Now to our final test: First of all, we’re using the Arquillian runner for jUnit here.

Arquillian allows us to decide what gets bundled for our test using the shrinkwrap API and the annotation @Deployment.

Setting testable to false here makes the tests run outside of the container.

@ArquillianResources returns the application URL valid for the duration of the test.

Our declarations in @BeforeClass force our tests to auto-authenticate as admin user, in our second test, we’re resetting this feature using RestAssured.reset().

In our first test, we’re using the slim dsl-like syntax of rest-assured to verify the response from the user-information REST-service response.

In the second test, we’re verifying the we’re redirected to the login mask when trying to access a restricted resource.

package it.webservice;
 
import static com.jayway.restassured.RestAssured.expect;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
 
import java.io.File;
import java.net.URL;
 
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.junit.InSequence;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
 
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.authentication.FormAuthConfig;
import com.jayway.restassured.config.RedirectConfig;
import com.jayway.restassured.config.RestAssuredConfig;
 
@RunWith(Arquillian.class)
public class AuthenticationWebserviceIT {
	@Deployment(testable = false)
	public static WebArchive createDeployment() {
		WebArchive archive = ShrinkWrap.create(WebArchive.class, "hascode.war");
		return archive.addPackages(true, "com.hascode").addAsResource("META-INF/persistence.xml").addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
				.setWebXML(new File("src/main/webapp/WEB-INF", "/web.xml")).addAsWebResource(new File("src/main/webapp/secured.html")).addAsWebResource(new File("src/main/webapp/login.html"));
	}
 
	@ArquillianResource
	protected URL appUrl;
 
	@BeforeClass
	public static void autoAuthAsAdministrator() {
		RestAssured.basePath = "/hascode/";
		RestAssured.config = RestAssuredConfig.config().redirect(new RedirectConfig().followRedirects(true).allowCircularRedirects(true));
		RestAssured.authentication = RestAssured.form("admin", "test", new FormAuthConfig("j_security_check", "j_username", "j_password"));
	}
 
	@Test
	@InSequence(1)
	public void shouldGetUserSessionInformation() throws Exception {
		expect().body("userid", equalTo("admin"), "password", equalTo(null), "groups.groupid", equalTo("administrators")).statusCode(200).when().get(appUrl + "rs/session");
	}
 
	@Test
	@InSequence(2)
	public void shouldLogoutSuccessfully() throws Exception {
		expect().statusCode(200).when().post(appUrl + "rs/session/logout");
		RestAssured.reset();
		expect().body(containsString("Login")).when().get(appUrl + "secured.html");
	}
}

We may run our tests now either using Maven or a test runner of choice…

Maven Test Execution

Running the tests with Maven is done with one command:

$ mvn test -Dtest=AuthenticationWebserviceIT
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.webservice.AuthenticationWebserviceIT
[..]
Results :
 
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
IDE with jUnit Runner

Running the tests with the jUnit runner in Eclipse IDE:

Testing the RESTful webservice with Arquillian and rest-assured

Testing the RESTful webservice with Arquillian and rest-assured

Sources and Downloads

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/javaee-jdbc-authentication-testing.git

Resources

Tags: , , , , , , , , , , , , , ,

One Response to “Java EE: Setting up and Testing Form-Based JDBC Authentication with Arquillian and Maven”

  1. John Waterwood Says:

    Nice, but would be even cooler if it used only standard technologies like JASPIC for authentication.

    One thing I don’t get is how domain.xml goes from META-INF/config to GlassFish. Does GlassFish really read it from there, or are we supposed to modify the one on GF, and just have the copy there for convenience? The text doesn’t explain this.

Search
Categories