Simple Spring Boot Microservices Architecture
Explanation:
-
API Gateway:
- The API Gateway serves as the entry point for all client requests. It routes requests to the appropriate microservice based on the URL patterns.
- It communicates with the Authorization Server to validate access tokens for secured endpoints.
-
Authorization Server:
- The Authorization Server is responsible for issuing OAuth2 tokens for authentication and authorization.
- It interacts with a User Database to validate user credentials.
-
Employee Service:
- This microservice handles CRUD operations related to employees.
- It communicates with the Employee Database to store and retrieve employee data.
- It requires a valid OAuth2 token from the Authorization Server to serve requests.
-
Department Service:
- This microservice handles CRUD operations related to departments.
- It communicates with a database (not explicitly shown in the diagram) to store and retrieve department data.
- It also requires a valid OAuth2 token from the Authorization Server to serve requests.
Data Flow:
- A client sends a request to the API Gateway.
- The API Gateway checks if the request is authenticated. If not, it redirects the client to the Authorization Server to obtain an access token.
- The Authorization Server validates the user credentials and issues an access token.
- The client includes the access token in subsequent requests to the API Gateway.
- The API Gateway routes the requests to the appropriate microservice (Employee Service or Department Service).
- The microservices validate the access token with the Authorization Server.
- Upon successful validation, the microservices process the request and interact with their respective databases to fetch or store data.
- The response is sent back to the API Gateway, which then forwards it to the client.
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:
- Authorization Server: Issues OAuth2 tokens.
- Employee Service: Manages employee information.
- API Gateway: Routes requests and handles security with OAuth2.
Step 1: Create the Authorization Server
1.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: auth-server
- Name: auth-server
- Description: OAuth2 Authorization Server
- Package Name: com.example.authserver
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click
Next
.
-
Select Dependencies:
- On the
Dependencies
screen, select the dependencies you need:- Spring Web
- Spring Security
- OAuth2 Authorization Server
- Spring Data JPA
- H2 Database
- Click
Next
.
- On the
-
Generate the Project:
- Click
Generate
to 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.
1.2 Update application.properties
Open the application.properties
file located in the src/main/resources
directory and add the following configuration:
server.port=9000
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
1.3 Create User Entity
Create a User
entity class in the com.example.authserver.model
package:
package com.example.authserver.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
// Getters and Setters
}
1.4 Create User Repository
Create a UserRepository
interface in the com.example.authserver.repository
package:
package com.example.authserver.repository;
import com.example.authserver.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
1.5 Create User Service
Create a UserService
class in the com.example.authserver.service
package:
package com.example.authserver.service;
import com.example.authserver.model.User;
import com.example.authserver.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder passwordEncoder;
@Autowired
public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public User saveUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
}
1.6 Create Security Configuration
Create a SecurityConfig
class in the com.example.authserver.config
package:
package com.example.authserver.config;
import com.example.authserver.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableAuthorizationServer
@EnableResourceServer
public class SecurityConfig extends ResourceServerConfigurerAdapter {
private final UserService userService;
@Autowired
public SecurityConfig(UserService userService) {
this.userService = userService;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
1.7 Create Authorization Server Configuration
Create an AuthorizationServerConfig
class in the com.example.authserver.config
package:
package com.example.authserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
@Autowired
public AuthorizationServerConfig(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client_id")
.secret("{noop}client_secret")
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "write")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(7200);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
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
Dependencies
screen, select the dependencies you need:- Spring Web
- Spring Data JPA
- Spring Security
- OAuth2 Resource Server
- H2 Database
- Click
Next
.
- On the
-
Generate the Project:
- Click
Generate
to 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
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9000/oauth/token_key
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.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.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.requestMatchers("/employees/**").authenticated()
.anyRequest().permitAll()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
}
Step 3: Create the API Gateway
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: 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
Dependencies
screen, select the dependencies you need:- Spring Cloud Gateway
- Spring Security
- OAuth2 Resource Server
- Click
Next
.
- On the
-
Generate the Project:
- Click
Generate
to 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.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/**
default-filters:
- TokenRelay
spring.security.oauth2.resourceserver.jwt.jwk-set-uri: http://localhost:9000/oauth/token_key
3.3 Create Security Configuration
Create a SecurityConfig
class in the com.example.apigateway.config
package:
package com.example.apigateway.config;
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.web.server.SecurityWebFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(HttpSecurity http) throws Exception {
http
.authorizeExchange(exchanges ->
exchanges
.pathMatchers("/employees/**").authenticated()
.anyExchange().permitAll()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
}
Step 4: Running the Microservices
4.1 Running Authorization Server
- Open the
AuthServerApplication
class in thesrc/main/java/com/example/authserver
directory. - Click the green
Run
button in your IDE or use the terminal to run the application:./mvnw spring-boot:run
4.2 Running Employee Service
- Open the
EmployeeServiceApplication
class in thesrc/main/java/com/example/employeeservice
directory. - Click the green
Run
button in your IDE or use the terminal to run the application:./mvnw spring-boot:run
4.3 Running API Gateway
- Open the
ApiGatewayApplication
class in thesrc/main/java/com/example/apigateway
directory. - Click the green
Run
button in your IDE or use the terminal to run the application:./mvnw spring-boot:run
Step 5: Testing the Application
You can use tools like Postman to test the authentication and secure access to the microservices.
5.1 Get Access Token
- Create a new
POST
request tohttp://localhost:9000/oauth/token
. - In the
Body
tab, selectx-www-form-urlencoded
and add the following parameters:grant_type
:password
client_id
:client_id
client_secret
:client_secret
username
:testuser
password
:testpassword
- Send the request and copy the access token from the response.
5.2 Access Secured Employee Service
- Create a new
GET
request tohttp://localhost:8080/employees
. - Add the
Authorization
header with the valueBearer <ACCESS_TOKEN>
. - Send the request and verify that you receive the list of employees.
Conclusion
In this tutorial, we created a microservices architecture with Spring Boot and secured it using OAuth2. We built an authorization server to issue tokens, secured the employee service with OAuth2, 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