Secure Spring Boot Rest API with Keycloak
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.
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
Now let’s open a browser and visit http://localhost:8180. We’ll be redirected to create an administrative login:
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.
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
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.
We’ll add a user name as “amila” with proper password.
Finally, we’ll navigate to the Role Mappings tab. We’ll be assigning the user role to our user.
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
In response, we’ll get an access_token and a refresh_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:
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.
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