How to Implement the Single Sign-On Pattern

Learn how to implement single sign-on in Java EE 8 in this tutorial by Rhuan Rocha, the author of Java EE 8 Design Patterns and Best Practices.

This tutorial shows an example of implementing single sign-on (SSO) where you’ll create the authentication service through a custom process to authenticate the users and will also allow the user to log in. After this, one token will be generated and sent to the user.

Further, you’ll create two applications (App1 and App2). When the user tries to access these applications without having logged in, the application will authenticate the user on the authentication service, so the user can access App1 and App2 without having to log in again.

The authentication service will be a REST application written using JAX-RS, and App1 and App2 will be applications that implement a JAX-RS client to validate user access. With this, the following classes will be created to use with the example:

  • AuthenticationResource: This is responsible for processing the login request and validating the authentication of a user. The class is written using JAX-RS and is inside the authentication service application.
  • AuthSession: This is a session that contains login data and information. This class has the application scope, that is, a Java EE scope.
  • Auth: This is a bean that represents the logged-in user. This class contains the user’s login details, password, and the date of last login.
  • TokenUtils: This is a class that contains a method for generating tokens.
  • App1: This app sends the Welcome to App1 text if the user is logged in. If the user is not logged in, the application launches an error.
  • App2: This app sends the Welcome to App2 text if the user is logged in. If the user is not logged in, the application launches an error.
  • Auth: This is an interface with methods responsible for calling the authentication services.
  • AuthImpl: This is an EJB class that implements the Auth interface.

The App1 and App2 applications don’t have any process or logic that is required in order to log a user in. This is the responsibility of the authentication service (the resource that validates the authentication), which has the AuthenticationResource class with this responsibility.

Implementing the AuthenticationResource class

AuthenticationResource is a JAX-RS resource, which allows logging in and validates the authentication of the application. The following code shows its implementation:

AuthenticationResource contains the authSession attribute used to persist the information about the login on the data source and obtain access to data sources that contain user information used to validate login credentials. Further, AuthenticationResource has two methods: login(String login, String password), is used to process the login request, and checkAuthentication( String token), used to allow clients to check whether a user is authenticated. In the following code block, you have the login method, which is used to log a user in:

You can see that if a user is already logged in, the token is returned as a response. If the user is not logged in, the login ID and password details are validated, and a new token is generated and returned as a response. Note that this method is called when the client sends a POST request to this resource.

The other method is checkAuthentication( String token), which is used to allow clients to check whether a user is authenticated. The method returns the 200 HTTP status code to the client if it is logged in, or returns the 401 HTTP status code if it is not logged in:

Note that the checkAuthentication(String token) method is called when the client sends a HEAD request.

The AuthSession class is used in the AuthenticationResource class. The AuthSession class has an application scope and is used to persist information about users that are logged in and has a data source that contains all the login credentials:

Auth is a bean that contains information about users’ login details:

As demonstrated, TokenUtils is a class that uses the generateToken() method to generate a new token:

Implementing the App1 and App2 classes

In the code block of the previous section, you have the code of the App1 application. When this application is accessed by a GET request, a request is sent to the authentication service to validate whether the user has already logged in:

In the above code, you have the App1 class, which contains the auth parameter, an EJB used to integrate with the authentication service. Further, this class has two methods, called helloWorld, with different signatures. In helloWorld( String login, String password ), the login is completed and then the Hello World. Welcome to App1! message is sent to the user. In helloWorld( String token ), the token is validated; if it is a valid token and the user is logged in, the Hello World. Welcome to App1! message is sent to the user.

The following code block is for the App2 class. The code is the same as that of App1 but prints a different message to the user:

The following code block contains the Auth interface. This interface details the contract with the methods responsible for integrating with the authentication service, validating the authentication, and logging in:

Here’s the code block for the AuthImpl class, which is an implementation of the Auth interface as well as a stateless EJB:

The above code block has three methods, called isLogged, login, and logout, with the signatures isLogged(String token), login(String login, String password), and logout(String token), respectively. When a user logs in to an application (either App1 or App2) and navigates to another application using the token, he/she won’t need to log in again.

That’s it! If you found this tutorial interesting, you can explore Java EE 8 Design Patterns and Best Practices to build enterprise-ready scalable applications with architectural design patterns. If you’re a Java developer wanting to implement clean design patterns to build robust and scalable applications, this book is a must-read!

Leave a Reply

Your email address will not be published. Required fields are marked *