Secure Spring Boot Rest API with Keycloak

Amila Iroshan
The Fresh Writes
Published in
8 min readMar 15, 2023

Overview

In this article, I’ll explain

  • How to Configure Keycloak Authorization Server
  • Use the admin Console of Keycloak for setting up and connecting to Spring Boot using the Spring Security OAuth2.0.
  • Secure Rest End point using Keycloak.

What is OAUTH 2.0 ?

Industry standard protocol, develop for authorization between services. The current version of this protocol is 2.0.Thats why its called as AOUTH 2.

What is KEYCLOAK

Key cloak is open source identity and access management tool(Authorization Server).It is a very easy way to secure applications and services with less code and less complexity. By integration of key cloak with your application you don’t want to deal with login forms, authenticating users and storing user’s details.

Secure REST API using keycloak

The main features of keycloak is as below.

Single-Sign On : Login once to multiple applications
Standard Protocols : OpenID Connect, OAuth 2.0 and SAML 2.0
Centralized Management : For admins and users
Adapters : Secure applications and services easily
LDAP and Active Directory : Connect to existing user directories
Social Login : Easily enable social login
Identity Brokering : OpenID Connect or SAML 2.0 IdPs
High Performance : Lightweight, fast and scalable
Clustering : For scalability and availability
Themes : Customize look and feel
Extensible : Customize through code
Password Policies : Customize password policies

Let’s start the implementation.

Step 01: Setting Up a Keycloak Server

First you have to download the key cloak binary zip via this : https://www.keycloak.org/

After unzip the folder and go to bin folder. Then open the cmd and run below command,

kc.bat start-dev — http-port=8180

Up the keycloak server via cmd

Now let’s open a browser and visit http://localhost:8180. We’ll be redirected to create an administrative login:

keycloak portal

Let’s create an initial admin user by providing preferred user credentials. Upon clicking Create, we’ll see the message User Created.

We can now proceed to the Administrative Console. On the login page, we’ll enter the given admin user credentials.

keycloak login

Creating a Realm

Here we’ll focus on creating a custom realm. After clicking the Create button, a new realm will be created and we’ll be redirected to it.

Creating a Client

Create Client

After navigate to the Create Clients, you can see page in the image below,

Below page should contain the application URL(s) that will use this client for authentication.

Later on, we’ll be creating a Spring Boot Application running at port 8080 that’ll use this client. Hence we’ve used a redirect URL of http://localhost:8080/* above.

Creating Role and Users

Keycloak uses Role-Based Access. Therefore, each user must have a role.

Create Role

We’ll add a user name as “amila” with proper password.

Create User
Set Password

Finally, we’ll navigate to the Role Mappings tab. We’ll be assigning the user role to our user.

user role mapping

Step 02 : Generating Access Tokens With Keycloak’s API

First, we need to acquire an access token from Keycloak by sending a POST request to this URL:
http://localhost:8180/realms/KeyCloak_Demo/protocol/openid-connect/token

The request should have this body

Request token

In response, we’ll get an access_token and a refresh_token.

Response token

Step 03 : Creating and Configuring a Spring Boot Application

Create spring boot application.

-Navigate to https://start.spring.io.

  • Choose either Gradle or Maven as build tool. In here I’m using Maven and Java 18.This is my dependencies on pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.oauth</groupId>
<artifactId>springboot-keycloak-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-keycloak-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>15.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
</build>
</project>

This is my project structure:

Project Structure

Keycloak Configuration

First create SecurityConfig class and add below annotations to enable keycloak authorization server and provide role base authorization.
@KeycloakConfiguration
@EnableGlobalMethodSecurity(jsr250Enabled = true)
@Import(KeycloakSpringBootConfigResolver.class)

Then extends KeycloakWebSecurityConfigurerAdapter class and override the configureGlobal(AuthenticationManagerBuilder auth) and configure(HttpSecurity http) methods by providing autherization provider details.

@KeycloakConfiguration
@EnableGlobalMethodSecurity(jsr250Enabled = true)
@Import(KeycloakSpringBootConfigResolver.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider authenticationProvider = new KeycloakAuthenticationProvider();
authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(authenticationProvider);
}
/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.anyRequest().permitAll();
}
}

This is my controller, dto and service classes respectively.

@RestController
@RequestMapping("/products")
public class controller {
@Autowired
private ProductService productService;

//this method can be accessed by user whose role is user
@GetMapping("/{productId}")
@RolesAllowed("user")
public ResponseEntity<Product> getProduct(@PathVariable int productId) {
return ResponseEntity.ok(productService.getProduct(productId));
}

//this method can be accessed by user whose role is admin
@GetMapping
@RolesAllowed("admin")
public ResponseEntity<List<Product>> findALlProducts() {
return ResponseEntity.ok(productService.getAllProducts());
}
}
public class Product implements Serializable{

private int id;
private String productName;
private int productQuantity;

public Product() {
super();
}
public Product(int id, String productName, int productQuantity) {
super();
this.id = id;
this.productName = productName;
this.productQuantity = productQuantity;
}
//Getters and Setters
}
@Service
public class ProductService {
List<Product> productsList=new ArrayList<>();
@PostConstruct
public void initializeProductMap() {
Product product_1=new Product(1, "RAM", 4);
Product product_2=new Product(2, "Mother_Board", 10);
Product product_3=new Product(3, "Key_Board", 20);
Product product_4=new Product(4, "Monitor", 12);
productsList.add(product_1);
productsList.add(product_2);
productsList.add(product_3);
productsList.add(product_4);
}
public Product getProduct(int productId) {
return productsList.stream().filter(p->p.getId() == productId).findFirst().orElse(new Product());
}
public List<Product> getAllProducts() {
return productsList;
}
}

Step 04 : Run & Check results

Run Spring Boot application with command: mvn clean spring-boot:run

Using postman you can access our secured REST endpoints by providing valid token as header parameter.

Access rest endpoint with user authorization permission
Access rest endpoint with admin authorization permission

You can find the complete source code of this example on GitHub

Thank you for reading this article and If you like this article, please do follow and clap 👏🏻.Happy coding, Cheers !!😊😊

Thanks for reading.Happy learning 😄

Do support our publication by following it

--

--

Amila Iroshan
The Fresh Writes

Software Engineer | Open Source Contributor | Tech Enthusiast