Search⌘ K
AI Features

Basic Authentication and PrimeFaces Methods

Explore how to implement form-based authentication in JakartaEE web applications using PrimeFaces. Learn to configure authentication with security constraints, create login forms with standard naming requirements, and handle user validation through CDI beans and the security API. Understand the setup of protected resources and testing methods for secure login flows.

Overview

In the chapter “Window Shopping,” we always used the Basic authentication method. However, many other standardized authentication methods are more helpful than this one. In the Basic authentication method, the end user has to fill in their credentials in a basic dialog. We want to present the user with a nice login form that can be styled according to the rest of the application. This can be achieved by the form-based authentication method. Other methods may be preferable depending on the application’s use case. These include OpenID Connect and identifications based on client certificates or tokens.

For the form-based method, a standard URL is defined within Jakarta Security, j_security_check, where the username and password (with standard names j_username and j_password) can be handled. The server picks up these values and validates them against a realm, as we did in the lesson Standards Basic Method.

Using OAuth2 and OpenID Connect requires an entirely different approach, as we will see in lessons OAuth2 and OpenID Connect. To get started, let’s look at the form-based authentication method for a web application.

PrimeFaces based form method

In this lesson’s example, we will use PrimeFacesPrimeFaces is a famous JavaServer Faces UI framework. It is used to develop sophisticated applications for enterprise or standard websites. is the default standard JSFJavaServer Faces component library, to integrate the requirements of form-based authentication. The standard URLs and post names for the credentials within a login page created with PrimeFaces.

We will define the protected resources using the security API configuration and indicate that we are using form based authentication using the standard names and endpoint.

We will define the protected resources using the security API configuration and indicate the form based authentication using the standard names and endpoint.

Create the project

We again start from the project template that we defined earlier and add the required dependencies and classes to it.

  • If you want to work within the Educative platform, simply use the project we’ve created at the end of this lesson. If you choose to work locally, you will need to create a Maven project formPF as described in “Introduction to Window Shopping."

Add Maven dependency

XML
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>be.rubus.workshop.security</groupId>
<artifactId>formPF</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>11.0.0</version>
</dependency>
</dependencies>
<build>
<finalName>formPF</finalName>
</build>
</project>

Explanation

  • Lines 23–27: Added the primefaces dependency.

Configure the protected resources

We now want to protect some Jakarta Faces pages, but there is no way to define the security constraints for them within the page itself the way we did in the lesson “Third-party Solution," with @ServletSecurity on the Servlet classes. We will therefore define the security constraints within the web.xml file.

Let’s go ahead and create the web.xml file within the WEB-INF directory.

XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<security-role>
<role-name>Users</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/protected/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>Users</role-name>
</auth-constraint>
</security-constraint>
</web-app>

Explanation

  • Lines 7–9: We define the roles that we will be using in the app.

  • Lines 11–23: We define the Security-constraint for the URL pattern /protected/*.

Configure the form authentication

We need to specify the page with the login elements and collect the username and password in the authentication method. For this purpose, we’ll once again use the Security API and the CDI bean that will define all the configurations.

Let’s go ahead and create the ApplicationConfiguration.java file within the form directory.

Java
package be.rubus.workshop.security.form;
import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition;
import javax.security.enterprise.authentication.mechanism.http.LoginToContinue;
@FormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage = "/login.xhtml",
errorPage = "/login-error.xhtml"))
@ApplicationScoped
public class ApplicationConfiguration {
}

Explanation

  • Lines 7–10: We define @FormAuthenticationMechanismDefinition on the class level, with configuration for the form-based authentication.

  • Lines 12 and 13: We create a class ApplicationConfiguration and define it as a CDI bean, so that configuration can be picked up by annotating the class with @ApplicationScoped.

Define login form page

Let’s go ahead and create login.xhtml within the webapp folder. (Facelet file in IntelliJ).

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
>
<h:head>
<title>Login</title>
</h:head>
<h:body>
<h:form id="login" onsubmit="action='j_security_check';" prependId="false">
<h:panelGrid columns="2">
<p:outputLabel for="j_username" value="Username"/>
<p:inputText id="j_username" name="j_username"/> <p:outputLabel for="j_password" value="Password"/>
<p:password id="j_password" name="j_password"/> <p:commandButton id="submit" value="Login" ajax="false"/>
</h:panelGrid>
</h:form>
</h:body>
</html>

Explanation

  • Lines 16–24: This is the content of the <h:body> tag.

Don’t forget to define the namespace alias here.

Define login-errorform page

Let's go ahead and create the login-error.xhtml page, also in the webapp folder.

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
>
<h:head>
<title>Login Problem</title>
</h:head>
<h:body>
The specified credentials are not valid, <a href="login.xhtml">try again</a>
</h:body>
</html>

Explanation

  • Lines 15–17: The specific credentials are not valid, <a href="login.xhtml">try again</a>.

Define users

We need to define some users and their roles to test out our example. We will use a dummy set by defining a custom Security API IdentityStore.

Let’s go ahead and create the TestIdentityStore.java file, in the form directory.

Java
/*
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package be.rubus.workshop.security.form;
import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.credential.UsernamePasswordCredential;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;
import java.util.HashSet;
import java.util.List;
import static java.util.Arrays.asList;
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
@ApplicationScoped
public class TestIdentityStore implements IdentityStore {
public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {
if (usernamePasswordCredential.compareTo("educative", "educative")) {
return new CredentialValidationResult("Rudy De Busscher", new HashSet<>(List.of("Users")));
}
return INVALID_RESULT;
}
}

Explanation

  • Line 29: We create a CDI bean by annotating it with @ApplicationScoped.

  • Line 30: We create the class TestIdentityStore and make sure it works within the interface javax.security.enterprise.identitystore.IdentityStore.

  • Line 32: We implement the validate method that validates user credentials.

Test page

Let’s go ahead and create the protected main.xhtml JSF page(Facelet file in IntelliJ) within the webapp/protected directory.

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Application</title>
</h:head>
<h:body>
Logged in user : #{request.remoteUser}
</h:body>
</html>

Explanation

  • Line 14: We use this to display the logged-in user.

Beans

Our application makes use of CDI beans. We need to indicate this by creating a beans.xml file.

  • Let’s go ahead and create the beans.xml within the src/main/webapp/WEB-INF directory.
  • We can leave this file empty (with no text at all, not even an XML header).

You probably want to have a proper configuration of the CDI system and define the XML header and namespace within your application.

Test

  • Build the application by executing the package goal of Maven:
mvn clean package
  • Run the application:
java -jar payara-micro.jar target/formPF.war

  • Enter the following URL in your browser after you hit “Run”:

{{EDUCATIVE_LIVE_VM_URL}}/formPF/protected/main.xhtml

Enter the username and password educative/educative.

We can also test out whether the error page works by providing the wrong credentials. We need to open another browser for this because the HTTP session and cookies will remember the user. Another option is simply to hit “Run” again to restart the application.

A working example is given below:

Note: We have pre-added all the commands in the widget below. You just have to hit “Run.” Click the link after “Your app can be found at” to view the app.

package be.rubus.workshop.security.form;

import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition;
import javax.security.enterprise.authentication.mechanism.http.LoginToContinue;

@FormAuthenticationMechanismDefinition(
        loginToContinue = @LoginToContinue(
                loginPage = "/login.xhtml",
                errorPage = "/login-error.xhtml"))

@ApplicationScoped
public class ApplicationConfiguration {
}
FormPF Application

Explanation

We need to add the Maven dependency to the project before we can use PrimeFaces within the application. No further configuration is needed to use widgets from the library in pom.xml.

  • Lines 7–10: In the ApplicationConfiguration.java file, we need to specify which URLs require authentication before we can access them. However, we can’t do this in the HTML or JSF pages as we did with the Servlets in “Window Shopping.” In the <security-constraint> element, we can instead define the URLs and the protected HTTP methods. When we want to use the form-based method, we need to specify the @FormAuthenticationMechanismDefinition annotation on a CDI bean.

The most important part of this example is the login page. As we’ve already discussed, there are specific requirements regarding the name of the fields and the URL to which we post this information. This means we have to use specific JSF features to make it compatible with these requirements.

  • Line 17: First, we need to override the URL where the form will POST the values. By default, this is always the same URL as the currently shown page, but it can’t be login.xhtml. Therefore, we change the URL in the onsubmit JavaScript callback, and we set the required j_security_check URL. Second, we need to ensure that the naming container ID doesn’t prefix all fields within the form. The “form” tag is a naming container in which the ID of all components are prepended. This helps the developer to give each component a unique ID. Here, we have to suppress this mechanism because the username and password field need to have specific IDs. We therefore need to use prependId="false" on the form tag in login.xhtml.

  • Lines 20 and 21: The input field for username and password can be anything as long as they have the correct ID/name. Here, we use the PrimeFaces specific component inputText and password in login.xhtml. The submit button needs to have the ID submit, as that is the name of the action of the form that is required for form-based authentication in login.xhtml.

  • Lines 13–15: We are using Jakarta EE standards for authentication, and we can ask the request what the authenticated user is. This is what we use in the test page main.xhtml where we show the logged-in user’s username.

  • Lines 15–17: If we specify some invalid credentials, the error page shown is login-error.xhtml. Here, we inform the user that their credentials are not recognized. We cannot and should not inform them exactly what went wrong. In fact, we should give away as little information as possible on what went wrong, as hackers can use that to retrieve some data.