Fork me on GitHub

Authentication Tutorial

This tutorial looks at configuring authentication.

WoOF provides various authentication schemes along with the ability to customise your own authentication scheme (see HttpSecuritySource for more details). This tutorial will focus on the most common authentication - form based authentication.

The below example for this tutorial will demonstrate only allowing a logged in user to view a page. The simple key pages for this tutorial are as follows:

AuthenticationHttpServer login screen shot.AuthenticationHttpServer hello screen shot.

Download Tutorial Source

Restricted access page

The page being restricted from access is as follows.

<html>
	<body>
		<p>Hi ${username}</p>
		<br />
		<p><a href="#{logout}">logout</a></p>
	</body>
</html>

With the backing logic class.

public class HelloLogic {

	@Data
	public static class TemplateData {

		private final String username;

	}

	public TemplateData getTemplateData(HttpSecurity security) {
		String username = security.getRemoteUser();
		return new TemplateData(username);
	}

	@NextTask("LoggedOut")
	public void logout(
			HttpAuthentication<HttpSecurity, HttpCredentials> authentication) {
		authentication.logout(null);
	}

}

The dependency on HttpSecurity requires the user to be logged in. Should the user not be authenticated, creation of this dependency will cause a HttpAuthenticationRequiredException to be thrown. WoOF automatically handles this exception by:

  1. saving the current request in the HTTP session
  2. send a challenge (in this case sending back the login page)
  3. authenticate the user (in this case validate the entered username and password)
  4. on authenticating the user, continue with the saved request

Since the getTemplateData requires a logged in user the page will not be rendered unless there is a logged in user.

To allow the page to be rendered with or without a logged in user, depend on HttpAuthentication to check if the user is logged in.

Configuring access

The following is the configuration for authentication.

Authentication configuration screen shot.

To specify the access, right click and select Set access from the menu. A wizard will appear to configure the appropriate HttpSecuritySource.

While some authentication schemes are straight forward (e.g. Basic), others such as form based login require application specific behaviour (e.g. a form login page). On selecting the authentication scheme, links necessary for the chosen authentication will be displayed for configuration. In the case of this tutorial, the form login link and authentication link are required to be configured to/from the login page. This allows the application to tailor the login page while still being able to re-use the FormHttpSecuritySource.

To enable differing credential stores (e.g. database, LDAP, etc), the WoOF supplied authentication depends on a CredentialStore managed object being configured. The following is the managed object configuration for this tutorial.

<objects>

	<managed-object source="net.officefloor.plugin.web.http.security.store.MockCredentialStoreManagedObjectSource" 
					type="net.officefloor.plugin.web.http.security.store.CredentialStore" />

</objects>

In this case a mock implementation is used that validates the user by ensuring the password matches the username. This is a simple implementation useful for testing.

For production, another CredentialStore should be used. WoOF comes with existing implementations for standard credential stores. Customised implementations may also be used for bespoke environments.

Login page

The login page is as follows.

<html>
	<head>
		<title>Login</title>
	</head>
	<body>
		<form action="#{login}" method="POST">
			Username: <input type="text" name="username" /> <br />
			Password: <input type="password" name="password" /> <br />
			<input type="submit" value="login" />
		</form>
	</body>
</html>

With the backing logic class.

public class LoginLogic {

	@Data
	@HttpParameters
	public static class Form implements Serializable {

		private String username;

		private String password;
	}

	@FlowInterface
	public static interface Flows {

		void authenticate(HttpCredentials credentials);
	}

	public void login(Form form, Flows flows) {
		flows.authenticate(new HttpCredentialsImpl(form.getUsername(), form
				.getPassword()));
	}

}

The FormHttpSecuritySource requires the credentials to be provided within a HttpCredentials as a parameter.

Remaining code

The remaining code is included for completeness.

Logout page

<html>
	<head>
		<title>Logout</title>
	</head>
	<body>
		<a href="#{hello}">Hello</a>
	</body>
</html>

Error page

<html>
	<body>
		An error occurred.
	</body>
</html>

Unit Test

The unit test demonstrates logging in and logging out.

	public void testLogin() throws Exception {

		// Start the server
		WoofOfficeFloorSource.start();

		// Ensure require login to get to page
		String loginPage = this
				.doHttpRequest("http://localhost:7878/hello.woof");
		assertTrue("Ensure login page",
				loginPage.contains("<title>Login</title>"));

		// Login
		String helloPage = this
				.doHttpRequest("https://localhost:7979/login-login.woof?username=Daniel&password=Daniel");
		assertTrue("Ensure hello page with login",
				helloPage.contains("<p>Hi Daniel</p>"));
	}

	public void testLogout() throws Exception {

		// Login (also starts server)
		this.testLogin();

		// Logout
		String logoutPage = this
				.doHttpRequest("http://localhost:7878/hello-logout.woof");
		assertTrue("Ensure logout page",
				logoutPage.contains("<title>Logout</title>"));

		// Attempt to go back to page (but require login)
		String loginPage = this
				.doHttpRequest("http://localhost:7878/hello.woof");
		assertTrue("Ensure login page",
				loginPage.contains("<title>Login</title>"));
	}

	private String doHttpRequest(String url) throws IOException {

		// Obtain the page
		HttpResponse response = this.client.execute(new HttpGet(url));
		String page = EntityUtils.toString(response.getEntity());

		// Return the page
		return page;
	}

Next

The next tutorial covers testing a WoOF web application.