Prerequisites
Before we start, ensure you have the following:
- Java Development Kit (JDK) installed
- Apache Maven installed
- Docker installed
- Docker Compose installed
- An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed
Overview of the Microservices
We will create three Spring Boot microservices:
- Employee Service: Manages employee information.
- Department Service: Manages department information.
- API Gateway: Routes requests and handles security with Keycloak.
We will also set up Keycloak as our identity provider.
Step 1: Setting Up Keycloak
1.1 Run Keycloak with Docker
Create a docker-compose.yml file to run Keycloak:
version: '3'
services:
keycloak:
image: quay.io/keycloak/keycloak:latest
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
command: start-dev
ports:
- "8080:8080"
Run the following command to start Keycloak:
docker-compose up -d
1.2 Configure Keycloak
-
Access Keycloak Admin Console:
- Open your web browser and go to
http://localhost:8080. - Log in with the username
adminand passwordadmin.
- Open your web browser and go to
-
Create a Realm:
- Click on the
Masterdrop-down on the top left corner and selectCreate Realm. - Name the realm
springboot.
- Click on the
-
Create a Client:
- In the
springbootrealm, go toClientsand clickCreate. - Name the client
springboot-clientand set the client type toOpenID Connect. - Set the Root URL to
http://localhost:8080and clickSave. - In the
Settingstab, setAccess Typetoconfidentialand setService Accounts EnabledtoON. - Copy the
Client Secretfrom theCredentialstab.
- In the
-
Create a Role:
- Go to
Rolesand clickAdd Role. - Name the role
userand clickSave.
- Go to
-
Create a User:
- Go to
Usersand clickAdd User. - Set the username to
testuserand clickSave. - In the
Credentialstab, set a password and turn offTemporary. - In the
Role Mappingstab, assign theuserrole to the user.
- Go to
Step 2: Create the Employee Service
2.1 Create a Spring Boot Project
-
Open Spring Initializr:
- Go to Spring Initializr in your web browser.
-
Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot 3.2
- Group: com.example
- Artifact: employee-service
- Name: employee-service
- Description: Employee Service
- Package Name: com.example.employeeservice
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click
Next.
-
Select Dependencies:
- On the
Dependenciesscreen, select the dependencies you need:- Spring Web
- Spring Data JPA
- Spring Security
- Keycloak Spring Boot Starter
- H2 Database
- Click
Next.
- On the
-
Generate the Project:
- Click
Generateto download the project zip file. - Extract the zip file to your desired location.
- Click
-
Open the Project in Your IDE:
- Open your IDE and import the project as a Maven project.
2.2 Update application.properties
Open the application.properties file located in the src/main/resources directory and add the following configuration:
server.port=8081
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
keycloak.realm=springboot
keycloak.auth-server-url=http://localhost:8080
keycloak.resource=springboot-client
keycloak.credentials.secret=YOUR_CLIENT_SECRET
keycloak.bearer-only=true
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/employees/*
Replace YOUR_CLIENT_SECRET with the client secret you copied from the Keycloak admin console.
2.3 Create Employee Entity
Create an Employee entity class in the com.example.employeeservice.model package:
package com.example.employeeservice.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String department;
// Getters and Setters
}
2.4 Create Employee Repository
Create an EmployeeRepository interface in the com.example.employeeservice.repository package:
package com.example.employeeservice.repository;
import com.example.employeeservice.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
2.5 Create Employee Controller
Create an EmployeeController class in the com.example.employeeservice.controller package:
package com.example.employeeservice.controller;
import com.example.employeeservice.model.Employee;
import com.example.employeeservice.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeRepository employeeRepository;
@Autowired
public EmployeeController(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
@GetMapping
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}
@PostMapping
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
}
2.6 Create Security Configuration
Create a SecurityConfig class in the com.example.employeeservice.config package:
package com.example.employeeservice.config;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests
.requestMatchers("/employees/*").hasRole("user")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public SimpleAuthorityMapper grantedAuthorityMapper() {
return new SimpleAuthorityMapper();
}
}
Step 3: Create the Department Service
3.1 Create a Spring Boot Project
-
Open Spring Initializr:
- Go to Spring Initializr in your web browser.
-
Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot 3.2
- Group: com.example
- Artifact: department-service
- Name: department-service
- Description: Department Service
- Package Name: com.example.departmentservice
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click
Next.
-
Select Dependencies:
- On the
Dependenciesscreen, select the dependencies you need:- Spring Web
- Spring Data JPA
- Spring Security
- Keycloak Spring Boot Starter
- H2 Database
- Click `Next
- On the
`.
-
Generate the Project:
- Click
Generateto download the project zip file. - Extract the zip file to your desired location.
- Click
-
Open the Project in Your IDE:
- Open your IDE and import the project as a Maven project.
3.2 Update application.properties
Open the application.properties file located in the src/main/resources directory and add the following configuration:
server.port=8082
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
keycloak.realm=springboot
keycloak.auth-server-url=http://localhost:8080
keycloak.resource=springboot-client
keycloak.credentials.secret=YOUR_CLIENT_SECRET
keycloak.bearer-only=true
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/departments/*
Replace YOUR_CLIENT_SECRET with the client secret you copied from the Keycloak admin console.
3.3 Create Department Entity
Create a Department entity class in the com.example.departmentservice.model package:
package com.example.departmentservice.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
}
3.4 Create Department Repository
Create a DepartmentRepository interface in the com.example.departmentservice.repository package:
package com.example.departmentservice.repository;
import com.example.departmentservice.model.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
3.5 Create Department Controller
Create a DepartmentController class in the com.example.departmentservice.controller package:
package com.example.departmentservice.controller;
import com.example.departmentservice.model.Department;
import com.example.departmentservice.repository.DepartmentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/departments")
public class DepartmentController {
private final DepartmentRepository departmentRepository;
@Autowired
public DepartmentController(DepartmentRepository departmentRepository) {
this.departmentRepository = departmentRepository;
}
@GetMapping
public List<Department> getAllDepartments() {
return departmentRepository.findAll();
}
@PostMapping
public Department createDepartment(@RequestBody Department department) {
return departmentRepository.save(department);
}
}
3.6 Create Security Configuration
Create a SecurityConfig class in the com.example.departmentservice.config package:
package com.example.departmentservice.config;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests
.requestMatchers("/departments/*").hasRole("user")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public SimpleAuthorityMapper grantedAuthorityMapper() {
return new SimpleAuthorityMapper();
}
}
Step 4: Create the API Gateway
4.1 Create a Spring Boot Project
-
Open Spring Initializr:
- Go to Spring Initializr in your web browser.
-
Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot 3.2
- Group: com.example
- Artifact: api-gateway
- Name: api-gateway
- Description: API Gateway
- Package Name: com.example.apigateway
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click
Next.
-
Select Dependencies:
- On the
Dependenciesscreen, select the dependencies you need:- Spring Cloud Gateway
- Spring Security
- Keycloak Spring Boot Starter
- Click
Next.
- On the
-
Generate the Project:
- Click
Generateto download the project zip file. - Extract the zip file to your desired location.
- Click
-
Open the Project in Your IDE:
- Open your IDE and import the project as a Maven project.
4.2 Update application.yml
Create an application.yml file in the src/main/resources directory and configure it as follows:
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: employee_service
uri: http://localhost:8081
predicates:
- Path=/employees/**
- id: department_service
uri: http://localhost:8082
predicates:
- Path=/departments/**
keycloak:
realm: springboot
auth-server-url: http://localhost:8080
resource: springboot-client
credentials:
secret: YOUR_CLIENT_SECRET
bearer-only: true
Replace YOUR_CLIENT_SECRET with the client secret you copied from the Keycloak admin console.
4.3 Create Security Configuration
Create a SecurityConfig class in the com.example.apigateway.config package:
package com.example.apigateway.config;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests
.requestMatchers("/employees/*", "/departments/*").hasRole("user")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public SimpleAuthorityMapper grantedAuthorityMapper() {
return new SimpleAuthorityMapper();
}
}
Step 5: Running the Microservices
5.1 Running Authentication Service
- Open the
AuthServiceApplicationclass in thesrc/main/java/com/example/authservicedirectory. - Click the green
Runbutton in your IDE or use the terminal to run the application:./mvnw spring-boot:run
5.2 Running Employee Service
- Open the
EmployeeServiceApplicationclass in thesrc/main/java/com/example/employeeservicedirectory. - Click the green
Runbutton in your IDE or use the terminal to run the application:./mvnw spring-boot:run
5.3 Running Department Service
- Open the
DepartmentServiceApplicationclass in thesrc/main/java/com/example/departmentservicedirectory. - Click the green
Runbutton in your IDE or use the terminal to run the application:./mvnw spring-boot:run
5.4 Running API Gateway
- Open the
ApiGatewayApplicationclass in thesrc/main/java/com/example/apigatewaydirectory. - Click the green
Runbutton in your IDE or use the terminal to run
the application:
./mvnw spring-boot:run
Step 6: Testing the Application
You can use tools like Postman to test the authentication and secure access to the microservices.
6.1 Get Access Token
- Create a new
POSTrequest tohttp://localhost:8080/realms/springboot/protocol/openid-connect/token. - In the
Bodytab, selectx-www-form-urlencodedand add the following parameters:client_id:springboot-clientusername:testuserpassword:testpasswordgrant_type:passwordclient_secret:YOUR_CLIENT_SECRET
- Send the request and copy the access token from the response.
6.2 Access Secured Employee Service
- Create a new
GETrequest tohttp://localhost:8080/employees. - Add the
Authorizationheader with the valueBearer <ACCESS_TOKEN>. - Send the request and verify that you receive the list of employees.
6.3 Access Secured Department Service
- Create a new
GETrequest tohttp://localhost:8080/departments. - Add the
Authorizationheader with the valueBearer <ACCESS_TOKEN>. - Send the request and verify that you receive the list of departments.
Conclusion
In this tutorial, we created a microservices architecture with Spring Boot and secured it using Keycloak. We built an authentication service to issue tokens, secured the employee and department services with Keycloak, and created an API Gateway to route and secure requests. This setup provides a solid foundation for developing secure microservices architectures.
Comments
Post a Comment