DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Aggregating REST APIs Calls Using Apache Camel
  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • Migrating Spring Java Applications to Azure App Service (Part 1: DataSources and Credentials)

Trending

  • Breaking Bottlenecks: Applying the Theory of Constraints to Software Development
  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • A Modern Stack for Building Scalable Systems
  • Performance Optimization Techniques for Snowflake on AWS
  1. DZone
  2. Coding
  3. Java
  4. Getting Started With Java and JSF

Getting Started With Java and JSF

Learn more about using JavaServerFaces (JSF) in your Java apps.

By 
Thiago Negri user avatar
Thiago Negri
·
Nov. 21, 19 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
17.5K Views

Join the DZone community and get the full member experience.

Join For Free

Man walking past a Wall of faces

Learn more about using JavaServerFaces (JSF) in your Java apps.

JavaServer Faces (JSF) is a Java framework standard for building out component-based user interfaces for web applications. JSF is supported by a wide range of ready-to-use components, libraries, tools, and vendors — making it even more powerful.

You may also like: [DZone Refcard] JavaServer Faces 2.0

Why choose JSF over JavaServer Pages (JSP)? There are two main reasons:

1. Templating Capabilities: JSF doesn’t directly allow you to write your view because it conflicts with the tag. You’ll preprocess the view before exporting to HTML by using XML, making it an efficient process as your code grows and is reusable.

2. MVC architecture: JSP abstracts writing a Servlet manually whereas JSF gives you access to an entire MVC architecture.

In this tutorial, we’re going to showcase JSF’s power by building a simple app that maintains a list of your favorite books. We’ll use Okta to manage user access and it’ll be backed up by a database.

Create a CRUD Application With JSF

To begin, we will use the TomEE Maven archetype to generate the project:

$ mvn archetype:generate \
    -DarchetypeGroupId=org.apache.openejb.maven \
    -DarchetypeArtifactId=tomee-webapp-archetype \
    -DarchetypeVersion=1.7.1


Follow the interactive generation process using these parameters to generate the app:

Define value for property 'groupId': com.okta.developer
Define value for property 'artifactId': jsf-crud
Define value for property 'version' 1.0-SNAPSHOT: : 1.0-SNAPSHOT
Define value for property 'package' com.okta.developer: : com.okta.developer
Confirm properties configuration:
groupId: com.okta.developer
artifactId: jsf-crud
version: 1.0-SNAPSHOT
package: com.okta.developer
Y: : Y


Then cd into the project, build and run to see it in action:

$ cd jsf-crud # or the artifactId you used to generate the project
$ mvn package
$ mvn tomee:run


Create a Book in Your JSF Application

Now point your preferred browser to http://localhost:8080/. You should see a form to create a book.

To add a book, just type the title and hit add. You will land on a success page and will be able to see a list of the books in the database. This form page is generated by src/main/webapp/book.xhtml, and the results page is generated by src/main/webapp/result.xhtml.

The book.xhtml is a simple form that connects its fields to the com.okta.developer.presentation.BookBean class. For example:

<h:inputText value='#{bookBean.bookTitle}'/>


For actions, like submitting the form, we bind directly to methods in the “bean” class. That specific form will trigger the add() method in the class:

<h:commandButton action="#{bookBean.add}" value="Add"/>


The BookBean.add() method creates a new Book instance and sets its title to what we have stored in bookTitle field (remember it was bound to the form’s input field):

public String add() {
    Book book = new Book();
    book.setBookTitle(bookTitle);
    bookService.addBook(book);
    return "success";
}


Then, it asks bookService to persist the book to the database as we can see in the com.okta.developer.application.BookService class:

public void addBook(Book book) {
  entityManager.persist(book);
}


But what about that "success" string being returned? It’s defined in the file src/main/webapp/WEB-INF/faces-config.xml:

<navigation-rule>
  <from-view-id>/book.xhtml</from-view-id>
  <navigation-case>
    <from-outcome>success</from-outcome>
    <to-view-id>/result.xhtml</to-view-id>
  </navigation-case>
</navigation-rule>


This means when a success happens on /book.xhtml file, JSF will send the user to the view /result.xhtml. In the result.xhtml file, we also see a button that binds to a method in the bean:

<h:commandButton action="#{bookBean.fetchBooks}" value="View books present"/>


This executes the method fetchBooks() in the class. In the src/main/java/com/okta/developer/presentation/BookBean.java file, we see fetchBooks() delegates to a method in bookService, which stores the results into the booksAvailable field, and then returns the string “success”.

public String fetchBooks() {
    booksAvailable=bookService.getAllBooks();
    return "success";
}


Read the Books in Your JSF Application

In the getAllBooks() method, the com.okta.developer.application.BookService class queries the database fetching all Book instances with no filters:

public List<Book> getAllBooks() {
    CriteriaQuery<Book> cq = entityManager.getCriteriaBuilder().createQuery(Book.class);
    cq.select(cq.from(Book.class));
    return entityManager.createQuery(cq).getResultList();
}


Cool. But how does the page actually display the book information? In the result.xhtml file, find the ui:repeat tag:

<ui:repeat value="#{bookBean.booksAvailable}" var="book">
    #{book.bookTitle} <br/>
</ui:repeat>


The <ui:repeat> tag iterates over each value, in this case #{bookBean.booksAvailable} is the field we just assigned from the fetchBooks() method. Each element of the collection can be referenced by the name in the var attribute of the tag (in this case book).

Whatever is inside the <ui:repeat> tag will be repeated for each of the elements in the collection. Here, it simply outputs the book title by using the interpolation notation #{book.bookTitle} followed by a line break tag (<br/>).

We just covered both the Create and Read methods of our CRUD application. Awesome! Now let’s try to Update a book.

Update a Record

Create an edit.xhtml file in src/main/webapp folder to contain the form to update a Book in the database. It looks very similar to the Create form:

<?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:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">

<h:body bgcolor="white">
    <f:view>
        <h1>Update book</h1>
        <h:form>
            <h:panelGrid columns="2">
                <h:outputText value='Enter book title'/>
                <h:inputText value='#{bookBean.bookTitle}'/>
                <h:outputText value='Update'/>
                <h:commandButton action="#{bookBean.update}" value="Update"/>
            </h:panelGrid>
            <input type="hidden" name="bookId" value='#{param.bookId}'/>
        </h:form>
    </f:view>
</h:body>
</html>


Now add a link to this page from the book list in file src/main/webapp/result.xhtml by changing the content of <ui:repeat> tag:

<ui:repeat value="#{bookBean.booksAvailable}" var="book">
    <h:link value="#{book.bookTitle}" outcome="edit">
        <f:param name="bookId" value="#{book.bookId}"/>
    </h:link>
    <br/>
</ui:repeat>


Now each book on the list is a link to that book’s edit page.

However, if you try to click on that link, you’ll see that the form shows empty on page load. Let’s fix that and load the book’s title before the form renders. To fetch the bookId from the URL in our bean, include the following dependency in the pom.xml file:

<dependencies>
  ...
  <dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>6.0</version>
    <scope>provided</scope>
  </dependency>
  ...
</dependencies>


Edit the src/main/java/com/okta/developer/application/BookService.java file and add the following method to BookService class to load the book from the database:

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Root;

...

public Book getBook(Integer bookId) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Book> cq = cb.createQuery(Book.class);
    Root<Book> book = cq.from(Book.class);
    cq.select(book);
    cq.where(cb.equal(book.get("bookId"), bookId));
    return entityManager.createQuery(cq).getSingleResult();
}


And add the following logic to BookBean to load the book before page render:

import javax.annotation.PostConstruct;
import javax.faces.context.FacesContext;

...

private Integer bookId;
private Book book;

@PostConstruct
public void postConstruct() {
    String bookIdParam = FacesContext.getCurrentInstance()
        .getExternalContext().getRequestParameterMap().get("bookId");
    if (bookIdParam != null) {
        bookId = Integer.parseInt(bookIdParam);
        book = bookService.getBook(bookId);
        bookTitle = book.getBookTitle();
    }
}


Now, let’s create a method that will persist the changes to the database, with the following method in the BookService class:

public void update(Book book) {
    entityManager.merge(book);
}


Also, add the update method to BookBean class:

public String update() {
    book.setBookTitle(bookTitle);
    bookService.update(book);
    return "success";
}


And to properly redirect the user to the list page, add the following navigation rule to the file src/main/webapp/faces-config.xml:

<navigation-rule>
  <from-view-id>/edit.xhtml</from-view-id>
  <navigation-case>
    <from-outcome>success</from-outcome>
    <to-view-id>/result.xhtml</to-view-id>
  </navigation-case>
</navigation-rule>


Now that we are done updating the book, let’s move on to the Delete part.

Delete a Record

The previous section covers the hard part — loading a book inside the bean. Adding the delete button will be easier.

Add a delete link for each entry in the list to the edit page in file src/main/webapp/result.xhtml:

<ui:repeat value="#{bookBean.booksAvailable}" var="book">
    <h:link value="#{book.bookTitle}" outcome="edit">
        <f:param name="bookId" value="#{book.bookId}"/>
    </h:link>

    <!-- Delete link: -->
    <h:outputText value=" ("/>
    <h:link value="Delete" outcome="delete">
        <f:param name="bookId" value="#{book.bookId}"/>
    </h:link>
    <h:outputText value=")"/>
    <br/>
</ui:repeat>


Now, create the delete confirmation page at src/main/webapp/delete.xhtml:

<?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:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">

<h:body bgcolor="white">
    <f:view>
        <h1>Delete book?</h1>
        <h:form>
            <h:panelGrid columns="2">
                <h:outputText value='Book title'/>
                <h:inputText value='#{bookBean.bookTitle}' readonly="true"/>
                <h:outputText value='Delete'/>
                <h:commandButton action="#{bookBean.delete}" value="Confirm Delete"/>
            </h:panelGrid>
            <input type="hidden" name="bookId" value='#{param.bookId}'/>
        </h:form>
    </f:view>
</h:body>
</html>


And add the proper delete handler in the BookBean class:

public String delete() {
    bookService.delete(book);
    return "success";
}


Next, handle the delete in the BookService class:

import javax.persistence.Query;

...

public void delete(Book book) {
    Query query = entityManager.createQuery("DELETE FROM Book b WHERE b.bookId = :bookId");
    query.setParameter("bookId", book.getBookId());
    query.executeUpdate();
}


And don’t forget to redirect your user back to the list after deletion by adding the following to src/main/webapp/faces-config.xml:

<navigation-rule>
    <from-view-id>/delete.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>success</from-outcome>
        <to-view-id>/result.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>


Done! I told you delete would be easier.

So now we can create, update, read and delete our Books CRUD application.

Improve the User Interface

The CRUD app works fine, but the app doesn’t look nice. Let’s improve our app user interface a bit with PrimeFaces.

First, add it as a dependency in our pom.xml:

<dependencies>
    ...
    <dependency>
        <groupId>org.primefaces</groupId>
        <artifactId>primefaces</artifactId>
        <version>7.0</version>
    </dependency>
    ...
</dependencies>


Now, we can use any PrimeFaces component in our views by declaring its namespace to our html tag, like so:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">


For a deeper overview, read about each PrimeFaces component in their site.

First, let’s remove the book list from our BookBean class and create a BookList class instead. This will load the book list as soon as the page loads. Create the file src/main/java/com/okta/developer/presentation/BookList.java with the following content:

package com.okta.developer.presentation;

import com.okta.developer.application.BookService;
import com.okta.developer.entities.Book;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

@Named
public class BookList {

    @Inject
    private BookService bookService;
    private List<Book> booksAvailable;

    @PostConstruct
    public void postConstruct() {
        booksAvailable = bookService.getAllBooks();
    }

    public List<Book> getBooksAvailable() {
        return booksAvailable;
    }
}


Remove the following blocks of code related to the booksAvailable field from BookBean class:

private List<Book> booksAvailable;

public List<Book> getBooksAvailable() {
    return booksAvailable;
}

public void setBooksAvailable(List<Book> booksAvailable) {
    this.booksAvailable = booksAvailable;
}

public String fetchBooks() {
    booksAvailable=bookService.getAllBooks();
    return "success";
}


Let’s also change our landing page. Instead of adding a Book through the form, let’s present the book list. For that, edit index.jsp to change the redirect to result.jsf:

<%@ page session="false" %>
<%
    response.sendRedirect("result.jsf");
%>


Here is what my files now look like. Feel free to adapt yours as you browse the PrimeFaces component library.

File: src/main/webapp/book.xhtml using a p:panel and p:panelGrid:

<?xml version="1.0" encoding="UTF-8"?>
<!-- File: book.xhtml -->
<!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"
      xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
    <h:form>
        <p:panel header="Create Book">
            <p:panelGrid columns="1" layout="grid">
                <p:outputLabel for="book-title" value="Enter book title"/>
                <p:inputText id="book-title" value='#{bookBean.bookTitle}'/>
                <p:commandButton value="Create" action="#{bookBean.add}" ajax="false"/>
            </p:panelGrid>
            <!-- We will use this later
            <input type="hidden" value="${_csrf.token}" name="${_csrf.parameterName}"/>
            -->
        </p:panel>
    </h:form>
</h:body>
</html>


File: src/main/webapp/delete.xhtml using a p:panel and p:panelGrid:

<?xml version="1.0" encoding="UTF-8"?>
<!-- File: delete.xhtml -->
<!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"
      xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
    <h:form>
        <p:panel header="Delete Book?">
            <p:panelGrid columns="1" layout="grid">
                <p:outputLabel for="book-title" value="Book title"/>
                <p:inputText id="book-title" value='#{bookBean.bookTitle}' readonly="true"/>
                <p:commandButton value="Confirm Delete" action="#{bookBean.delete}" ajax="false"/>
            </p:panelGrid>
            <input type="hidden" name="bookId" value='#{param.bookId}'/>
            <!-- We will use this later
            <input type="hidden" value="${_csrf.token}" name="${_csrf.parameterName}"/>
            -->
        </p:panel>
    </h:form>
</h:body>
</html>


File: src/main/webapp/edit.xhtml using a p:panel and p:panelGrid:

<?xml version="1.0" encoding="UTF-8"?>
<!-- File: edit.xhtml -->
<!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"
      xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
    <h:form>
        <p:panel header="Update Book">
            <p:panelGrid columns="1" layout="grid">
                <p:outputLabel for="book-title" value="Enter new book title"/>
                <p:inputText id="book-title" value='#{bookBean.bookTitle}'/>
                <p:commandButton value="Update" action="#{bookBean.update}" ajax="false"/>
            </p:panelGrid>
            <input type="hidden" name="bookId" value='#{param.bookId}'/>
            <!-- We will use this later
            <input type="hidden" value="${_csrf.token}" name="${_csrf.parameterName}"/>
            -->
        </p:panel>
    </h:form>
</h:body>
</html>


File: src/main/webapp/result.xhtml using a p:dataList instead of ui:repeat:

<?xml version="1.0" encoding="UTF-8"?>
<!-- File: result.xhtml -->
<!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:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
    <h:link outcome="book" value="Create Book"/>
    <p:dataList value="#{bookList.booksAvailable}" var="book" type="ordered">
        <f:facet name="header">
            Book List
        </f:facet>
        #{book.bookTitle}
        <h:outputText value=" ("/>
        <p:link value="Edit" outcome="edit">
            <f:param name="bookId" value="#{book.bookId}"/>
        </p:link>
        <h:outputText value=" | "/>
        <p:link value="Delete" outcome="delete">
            <f:param name="bookId" value="#{book.bookId}"/>
        </p:link>
        <h:outputText value=")"/>
    </p:dataList>
</h:body>
</html>


Run the Application With PrimeFaces Enabled

Restart the app with mvn package tomee:run. The app will look a bit better now! Check out the book list:

Book list with PrimeFaces

Secure Your Application With Okta

At the moment, anyone can access our awesome Book application and change the database. To prevent that, let’s add a security layer to our application with the Spring Security library and authenticate users through Okta.

First, register for a forever-free developer account today! When you’re finished, complete the steps below to create an OIDC app.

  1. Log in to your developer account at developer.okta.com
  2. Navigate to Applications and click on Add Application
  3. Select Web and click Next
  4. Give the application a name (.e.g., Java JSF Secure CRUD)
  5. Add the following as Login redirect URI:
    • http://localhost:8080/login/oauth2/code/okta
  6. Click Done

Now, create the file src/main/resources/application.properties with your Client ID and Client Secret, you can find these on the General tab of the app you just created.

okta.client-id={clientId}
okta.client-secret={clientSecret}
okta.issuer-uri=https://{yourOktaDomain}/oauth2/default


Let’s add Spring Security as a dependency in your pom.xml file:

<properties>
    ...
    <spring-security.version>5.1.6.RELEASE</spring-security.version>
    <spring.version>5.1.6.RELEASE</spring.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>${spring.version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-bom</artifactId>
            <version>${spring-security.version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    ...
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-resource-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-jose</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.9</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.9.3</version>
    </dependency>
</dependencies>


For Spring Security to correctly control your app security, it needs to know which requests need authentication. For that, we will create the file src/main/java/com/okta/developer/SecurityConfiguration.java to tell Spring Security to secure all URLs and to use a CSRF token to secure forms:

package com.okta.developer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private final String clientSecret;
    private final String clientId;
    private final String issuerUri;

    @Autowired
    public SecurityConfiguration(@Value("${okta.issuer-uri}") String issuerUri,
                                 @Value("${okta.client-id}") String clientId,
                                 @Value("${okta.client-secret}") String clientSecret) {
        this.issuerUri = issuerUri;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement() // Always create a session
                .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
                .and()
            .csrf() // Use CSRF token
                .csrfTokenRepository(new CookieCsrfTokenRepository())
                .and()
            .authorizeRequests() // Require all requests to be authenticated
                .anyRequest().authenticated()
                .and()
            .oauth2Login();
    }

    /* Following methods instruct Spring Security to use 
       Okta as user/authentication provider 
    */

    @Bean
    public OAuth2AuthorizedClientService authorizedClientService() {
        return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository());
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        ClientRegistration okta = getRegistration();
        return new InMemoryClientRegistrationRepository(okta);
    }

    private ClientRegistration getRegistration() {
        return ClientRegistrations.fromOidcIssuerLocation(this.issuerUri)
                .registrationId("okta")
                .clientId(this.clientId)
                .clientSecret(this.clientSecret)
                .build();
    }
}


Next, create the file src/main/java/com/okta/developer/SecurityWebApplicationInitializer.java class to enable Spring Security in the app:

package com.okta.developer;

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
   extends AbstractSecurityWebApplicationInitializer {

   public SecurityWebApplicationInitializer() {
       super(SecurityConfiguration.class);
   }
}


Since we have enabled CSRF protection, we need to add the token to each <h:form> tag. Just add that line within the forms (I left those commented out on my files that use PrimeFaces):

<input type="hidden" value="${_csrf.token}" name="${_csrf.parameterName}"/>


Done! Go to http://localhost:8080 and you will be redirected to an Okta login form and will only be able to use the application after you are successfully authenticated.

Want to share the app with a friend? Cool, go to the Okta developer console page, go to Users and create an account for them. You now have a fully functional security administration tool as well, where you can enable/disable users, check when they are logging to your app, reset their passwords, etc.

Enjoy your new, secure Book app!

Learn More About Java, JSF and User Auth!

If you want to learn more about Java, JSF and User Auth using Okta, we have other awesome articles for you to keep reading:

  • Build a Java REST API with Java EE and OIDC
  • Which Java SDK Should You Use?
  • Tutorial: Create and Verify JWTs in Java
  • Learning Java as a First Language

Questions? Requests for a future post? Drop them in the comments! And don’t forget to follow @oktadev on Twitter and subscribe on Youtube.

Build a Simple CRUD App with Java and JSF was originally published on the Okta Developer Blog on September 27, 2019.

Java (programming language) Database Spring Security Book app application Form (document) Spring Framework

Published at DZone with permission of Thiago Negri. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Aggregating REST APIs Calls Using Apache Camel
  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • Migrating Spring Java Applications to Azure App Service (Part 1: DataSources and Credentials)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!