Simple Java EE (JSF) Login Page with JBoss PicketLink Security
Several years ago I wrote a tutorial about using Acegi/Spring Security with JavaServer Faces (JSF) to create a simple authentication / Login page; however, times have changed and Java EE is back in action. I would no longer consider Spring a “requirement” when building a Java EE application. More specifically, if you are using the core Contexts and Dependency Injection (CDI) framework that serves as the backbone for the entire Java EE framework, Spring Security becomes less attractive (because it’s not compatible without using Spring itself, and Spring is a replacement for CDI).
This article will explore how to create a JSF login backed by the standards-compliant CDI framework (that is included with Java EE), and the PicketLink security framework (an open-source project from JBoss). Examples for this article were sourced from the very comprehensive, and quite understandable quick-start application from the PicketLink project itself.
Set up the Application
First things first, you’ll need to create a new Maven project set up as a Java EE web-application. You can do this using JBoss Forge, or you can just download the quick-start code, skip all the setup, and jump right to the explanation.
Activate JSF and CDI
You will also want to make sure that you have activated both JSF and CDI in your application configuration, so make sure that you do not forget to add the
Add PicketLink to pom.xml
This step is as straightforward as the rest of this tutorial – just add the following configuration to your maven pom.xml file:
<dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-servlet</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-integration-faces</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
Create the security configuration
This is where we will set up PicketLink, and tell it where our authentication page lives. This file should be placed anywhere on your Java source path.@RewriteConfiguration public class ExampleConfigurationProvider extends HttpConfigurationProvider { @Override public int priority() { return 10; } @Override public Configuration getConfiguration(final ServletContext context) { return ConfigurationBuilder.begin() .addRule(Join.path("/").to("/home.jsf")) .addRule(Join.path("/login").to("/login.jsf")) .addRule(Join.path("/error").to("/error.jsf")); } }
This is essentially a simple CDI observer for the org.picketlink.event.SecurityConfigurationEvent
. The event is fired during application startup and allows you to provide any configuration to PicketLink before it is initialized. All configuration related to Http Security is handled in this bean.
In order for this configuration to recognize users, however, we’ll need to add some data to our IdentityManager
. The IdentityManager
is part of the PicketLink API that controls user credentials, passwords, and integration with your database (if you use one ;). To access this API, you should inject the PartitionManager
into a @Startup, @Singleton
bean.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <filter> <filter-name>OCPsoft Rewrite Filter</filter-name> <filter-class>org.ocpsoft.rewrite.servlet.RewriteFilter</filter-class> <async-supported>true</async-supported> </filter> <filter> <filter-name>PicketLink Security Filter</filter-name> <filter-class>org.picketlink.http.internal.SecurityFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>OCPsoft Rewrite Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ASYNC</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> <filter-mapping> <filter-name>PicketLink Security Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ASYNC</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> </web-app>
This startup bean creates a default user account when the application is started. Since we are not providing an IDM configuration in this example, PicketLink will default to using a file-based identity store to persist user and other identity state.
Once we have this set up, our application is now ready to get a face-lift! It’s time to build the JSF pages that will use PicketLink to authenticate the user.
Create the Login Page
As we defined in our
This file contains a simple JSF form and command button that submits to PicketLink.
<h:form method="POST" prependId="false">
. Otherwise, the form fields will be prepended with a random JSF component ID, like “j_3234:j_password”, and picketlink will not be able to handle the request!<dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-servlet</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-integration-faces</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
Create a public and private page for the user to access
We also need to do the same for our server error page (where the user will be directed if login fails.)
@RewriteConfiguration public class ExampleConfigurationProvider extends HttpConfigurationProvider { @Override public int priority() { return 10; } @Override public Configuration getConfiguration(final ServletContext context) { return ConfigurationBuilder.begin() .addRule(Join.path("/").to("/home.jsf")) .addRule(Join.path("/login").to("/login.jsf")) .addRule(Join.path("/error").to("/error.jsf")); } }
Last but not least, we need to add a few pages for the user to try to access, for which authentication is required, and the login form will be displayed before access is granted!
The home page is a public page, and can be accessed by any user
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <filter> <filter-name>OCPsoft Rewrite Filter</filter-name> <filter-class>org.ocpsoft.rewrite.servlet.RewriteFilter</filter-class> <async-supported>true</async-supported> </filter> <filter> <filter-name>PicketLink Security Filter</filter-name> <filter-class>org.picketlink.http.internal.SecurityFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>OCPsoft Rewrite Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ASYNC</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> <filter-mapping> <filter-name>PicketLink Security Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ASYNC</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> </web-app>
Files in the protected
folder are secured by PicketLink (default configuration), and cannot be accessed until the user has successfully logged in:
<dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-servlet</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-integration-faces</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
Try it out!
It’s now time to deploy the application to WildFly (a Java EE standards compliant web-server), where you can try the application for yourself!
How did it go? What problems did you run in to? How could this example be made simpler? Let us know! For more examples on how to use PicketLink with many other frameworks like 2-factor authentication, SSO with Twitter, Google, and Facebook, check out the rest of the quickstarts: https://github.com/jboss-developer/jboss-picketlink-quickstarts
Thanks for that!
I’ve got an app running on Wildfly 9, jsf/primefaces. I’m using form-based authentication using the standard security module, not picketlink (at least not directly, I’m not sure if the standard wf uses it by default.)
Anyway, I’m wondering what your logout code looks like? I’ve got a problem where users often have to hit the logout button twice before it logs them out.
I’ve got a servlet, mapped to a url pattern in web.xml, simple and old school, that just does this:
String contextPath = request.getContextPath();
request.logout();
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
and then it redirects to the (protected) home page, which causes the login page to appear half the time. But the other half the time it just goes right through to the home page. Then if you click Logout a second time it works.
I know this isn’t a support column! But I’m just asking, since you posted login code, could you just please post your logout code?
thanks
Michael Davis
Ottawa
Thanks very much,
Oops – looks like I copy/pasted it wrong – of course the logout call comes after the request.getSession()!
Hey Michael,
I’m glad you’re finding this helpful! The logout code is in the HttpSecurityConfiguration:
Maybe it is interesting to note that when using components like PrimeFaces, RichFaces, IceFaces, OpenFaces, AngularFaces (if I forgot one, please let me know), that adding
is ‘required’ to have these components work and look good in an unprotected login page
Hey Ronald,
Yep! That’s a good point. Un-restricting the JSF resource path is important to make sure that images, stylesheets, and javascript can still be loaded properly from the browser. I’ll update that.
~Lincoln
How to get current authenticated account in a cdi bean ?
I’m having the following problem:
The User is with the expired session and fills the data from the form and send.
I need to recover this last action to perform after login with the same information form.
Hi,
Does PicketLink play nice with Rewrite? If I configure PicketLink with something like …authenticateWith().form().loginPage("/login.xhtml") and have some rewriting rule /login.xhtml -> /login, PicketLink redirects on /login.xhtml and not /login.
This could occur if PicketLink SecurityFilter comes before RewriteFilter (which wraps the response) or if PicketLink does not call response.encoreRedirectUrl(). Any idea?
Thanks,
Xavier
I’ve been searching for about a month for a example of how to use Picketlink with Glassfish and i’ve got nothing. You guys could provide us with some.