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
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
formPFas described in “Introduction to Window Shopping."
Add Maven dependency
Explanation
Lines 23–27: Added the
primefacesdependency.
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.
Explanation
Lines 7–9: We define the roles that we will be using in the app.
Lines 11–23: We define the
Security-constraintfor 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.
Explanation
Lines 7–10: We define
@FormAuthenticationMechanismDefinitionon the class level, with configuration for the form-based authentication.Lines 12 and 13: We create a class
ApplicationConfigurationand 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).
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.
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.
Explanation
Line 29: We create a CDI bean by annotating it with
@ApplicationScoped.Line 30: We create the class
TestIdentityStoreand make sure it works within the interfacejavax.security.enterprise.identitystore.IdentityStore.Line 32: We implement the
validatemethod 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.
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.xmlwithin thesrc/main/webapp/WEB-INFdirectory. - 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 {
}
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.javafile, 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 theServletsin “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@FormAuthenticationMechanismDefinitionannotation 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 theonsubmitJavaScript callback, and we set the requiredj_security_checkURL. 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 useprependId="false"on the form tag inlogin.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
inputTextandpasswordinlogin.xhtml. The submit button needs to have the IDsubmit, as that is the name of the action of the form that is required for form-based authentication inlogin.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.xhtmlwhere 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.