Communicating with identity and access management systems is a common task for many web-applications exposing secured resources.
Keycloak is an open source software that provides not also such authorization services but also offers a lot of features from Single-Sign-On, Identity-Brokering, Social-Login, User-Federation, multiple client-adapters up to the administration console or support for protocols like OpenID, SAML, OAuth2, Kerberos and more.
I will demonstrate how to integrate a Spring Boot web application with Keycloak and configure an authentication flow that requires a two-factor-authentication with user credentials and also one-time-passwords.
What we’re going to build
In the following tutorial we will install a fresh new Keycloak server instance and configure realms, roles, users as well as one-time-password (OTP) authentication step by step.
Afterwards we’ll implement a web application with Spring Boot that exposes some secured resources to authenticated users within a specific role and communicates with our Keycloak server instance to authenticate users.
Installing and Starting Keycloak
Keycloak is easy to install, either by downloading the binaries from the Keycloak website or using Docker.
Manual installation
Download the binaries from the Keycloak website and run the following command to start the server:
$ sh ./bin/standalone.sh
Using Docker
Simply use the Docker image jboss/keycloak from Docker Hub here:
$ docker run jboss/keycloak
We should be able now to access our Keycloak server by pointing our browser to the address http://localhost:8080/auth.
Further details can be found in the excellent documentation on the Keycloak website here: "Server Installation and Configuration".
If not interested in the details of the Keycloak configuration, please skip to the Spring Boot section of this tutorial.
Keycloak Setup
Now that we’ve got a running server we need to add some configuration like realms, users, roles and security configuration..
Creating the Administration Account
When running Keycloak for the first time, we’re required to create an administration account and set a password for this administrator.
Then we’re able to log into the master-realm at http://localhost:8080/auth/admin/ using these credentials.
Creating a new Realm
We’re creating a new realm that will be used for our web-application named "tutorial".
Creating a new Client
In the next step, we’re creating a new client named "hascode-tutorial-app".
In the client configuration screen we now need to specify a URL pattern for Valid Redirect URLs that we’re setting to http://localhost:8081/* (we’ll be running our web-application at this address and port later..):
Creating a new Role
Create new role named "user". In our Spring Boot application we’ll be restricting access to a specific page to users with that role.
Creating a new User with Credentials and Role Assignments
We’re now creating a new user with username "lisa", for now we don’t need to add any other information here.
In the user credentials screen we’re adding a new password of choice for the user and we’re disabling the setting for temporary credentials as we do not want our user to change his password on first login.
Finally in the tab named "Role Mappings” we’re assigning our user "lisa” to the role "user".
Now we’re done with the basic configuration and just need to fine-tune the settings for one-time-passwords.
One-Time-Password (OTP) Configuration
In our realm configuration in the "Authentication” settings we’re modifying our "Flows” configuration and we’re setting the value of OTP Form to "required".
In the tab "OTP Policy” we’re updating the number of digits for our OTP code to 8 digits.
Installing an OTP Client
Now we need and OTP client that acts as a key generator and may be installed on our smartphone.
Three apps for Android that I know are..
Most times I am using FreeOTP and I have tested the Keycloak interaction with this app.
Testing OTP Secured Access
Having all we need for now, we may now start a manual test of our security strategy.
We’re accessing the account view of our "tutorial" realm by accessing the following URL in our browser: http://localhost:8080/auth/realms/tutorial/account/.
In the screen, we’re entering our credentials "lisa” as username and our password.
In the following screen we’re required to setup our OTP key generator.
This is done by simply scanning the provided QR code with our OTP app and entering the generated code.
We should then be redirected to the Keycloak account configuration.
As we’re now sure that everything is working as expected on the Keycloak side, we’re ready to implement a connected web application.
Creating a secured Spring Boot Web Application
We’ll be implementing two different solutions with Spring Boot: one simple application using only spring-boot web and another one using spring-security.
There is an excellent article available in the RedHat Developer Blog (as many others) that describes in-detail how to configure Spring Boot with Keycloak and I highly recommend reading it.
In addition there is a dedicated chapter in the Keycloak documentation about the Spring Boot Adapter.
Project Setup
We’re creating a new Spring Boot application (using Spring Initializr) with the following additional (to the parent pom) dependencies:
-
spring-boot-starter-web
-
keycloak-spring-boot-starter
-
spring-boot-starter-thymeleaf
So our shortened pom.xml looks similar to this one:
<project>
[..]
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
[..]
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
[..]
</project>
The complete descriptor file is available in my repository here.
Controller
In our controller class, we’re defining a data-transfer-object for articles and two actions to render a list of blog articles and to logoff a user.
To logout a user we’re accepting the HttpServletRequest as method-parameter and use its logout method.
package com.hascode.tutorial;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
class ArticleController {
static class Article {
private String title;
private String url;
public Article(String title, String url) {
this.title = title;
this.url = url;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
}
@GetMapping(path = "/articles")
public String getProducts(Model model) {
model.addAttribute("articles", Arrays.asList(
new Article("Microbenchmarks with JMH / Java Microbenchmark Harness",
"https://www.hascode.com/2017/10/microbenchmarks-with-jmh-java-microbenchmark-harness/"),
new Article(
"Resilient Architecture in Practice – Circuit Breakers for Java: Failsafe, Javaslang, Hystrix and Vert.x",
"https://www.hascode.com/2017/02/resilient-architecture-circuit-breakers-for-java-hystrix-vert-x-javaslang-and-failsafe-examples/"),
new Article("Assuring Architectural Rules with ArchUnit",
"https://www.hascode.com/2017/07/assuring-architectural-rules-with-archunit/")));
return "articles";
}
@GetMapping(path = "/logout")
public String logout(HttpServletRequest request) throws ServletException {
request.logout();
return "logout";
}
}
Application
Our application class contains the usual minimalistic Spring Boot setup, nothing special.
package com.hascode.tutorial;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}
}
Static Start Page
This is the static HTML file that our application serves as the start page:
<html>
<head>
<title>hasCode.com Sample Spring Boot App</title>
</head>
<body>
<h1>hasCode.com Sample Spring Boot App</h1>
<a href="/articles">Articles (requires authentication)</a>
</body>
</html>
Thymeleaf Articles Template
This is our Thymeleaf template to display a list of given blog articles.
More detailed information about the Thymeleaf template engine syntax can be found in its documentation.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>hasCode.com Articles</title></head>
<body>
<h1>hasCode.com Articles</h1>
<ul>
<li th:each="article : ${articles}">
<a th:href="${article.url}" th:text="${article.title}"/>
</li>
</ul>
<p>
<a href="/logout">Logout</a>
</p>
</body>
</html>
Spring Configuration
Finally we need to tell our application how to address Keycloak, especially by adding the following entries to our applications application.properties:
-
keycloak.auth-server-url: The address of our Keycloak server
-
keycloak.realm: The selected realm’s name
-
keycloak.public-client: Public client setting
-
keycloak.resource: Name of the resource
-
keycloak.security-constraints[0].authRoles[0]: Name of the required role
-
keycloak.security-constraints[0].securityCollections[0].patterns[0]: Pattern of the secured resource
In addition to the settings above, we’re adding a server.port entry set to 8081 because port 8080 is already used by Keycloak.
So our final config file should look similar to this one:
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.realm=tutorial
keycloak.public-client=true
keycloak.resource=hascode-tutorial-app
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/articles/*
server.port=8081
Running the Application
Finally we’re ready to start our application and find out if the integration with Keycloak is working as expected.
We’re starting our application by running the following command in the command-line:
$ mvn clean spring-boot:run
Afterwards we should be able to access our application in the browser by pointing it at http://localhost:8081/.
We should see the start-page as shown in the following screenshot:
Clicking on "Articles" redirects us to the Keycloak Login Screen, where we’re entering our credentials and then are asked to enter our OTP code from the app:
With valid credentials we’re now redirected to our application’s articles overview:
The logout action terminates our authentication and we’re redirected to the logout page.
Alternative with Spring Security
In a real production app, there is often Spring Security involved that’s why we’re modifying our existing application to work with Spring Security now.
To make it work we only need to apply one dependency, modify our configuration file and add a configuration class to the application.
Dependencies
First of all to address Spring Security we need to add its dependency to our project by adding the following entry to our project’s pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Spring Configuration
In the next step we may remove the following two entries from our application.properties config file as we’ll be handling necessary roles and secured URL schemes with Spring Security mechanisms in our security configuration class.
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/articles/*
Security Configuration
Last but not least we’re adding the following security configuration class to our application.
Here we’re exposing instances of KeycloakConfigResolver and SessionAuthenticationStrategy for injection and we’re adding global security configuration as well as role-restrictions for our articles view.
package com.hascode.tutorial;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/articles*").hasRole("user")
.anyRequest().permitAll();
}
}
That’s all that we needed to change and when re-running our application we should be able to run the same authentication flow as in our simple version.
Disabling the Keycloak Spring Boot Adapter
It’s easy to disable the Keycloak adapter e.g. for testing purpose by adding the following item to our Spring configuration (application.properties):
keycloak.enabled = false
Tutorial Sources
Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:
git clone https://github.com/hascode/keycloak-springboot-otp.git
Resources
OAuth2 Identity/Resources Server with Spring Boot
When interested in implementing identity as well as resource servers with Spring Boot, please feel free to visit the following article of mine: "Setting up an OAuth2 Authorization Server and Resource Provider with Spring Boot".