A comprehensive Spring Boot microservices architecture demonstrating JWT-based authentication, API Gateway routing, and event-driven communication using RabbitMQ.
- Architecture Overview
- Technology Stack
- Services Description
- Project Structure
- Database Schema
- Event-Driven Architecture
- API Gateway Routes
- Prerequisites
- Setup and Installation
- Configuration
- Running the Application
- API Endpoints
- Event Flow
- Security Implementation
- Testing
- Troubleshooting
This project implements a microservices architecture with the following components:
βββββββββββββββββββ
β API Gateway β (Port 8080)
β JWT Validation β
ββββββββββ¬βββββββββ
β
ββββββ΄βββββββββββββββββββββ¬βββββββββββββββ
β β β
βββββΌβββββββββ ββββββββββΌβββββ ββββββΌβββββββββββββββ
β User β β Order β β Payment/Invoice β
β Service β β Service β β Service β
β (Port 8081)β β (Port 8083) β β (Port 8082) β
βββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββββββββ
β β β
ββββββββββββββββββββββΌβββββββββββββββββββ
β
ββββββββΌβββββββ
β RabbitMQ β
β Message β
β Broker β
βββββββββββββββ
β
ββββββββΌβββββββ
β PostgreSQL β
β Database β
βββββββββββββββ
- JWT Authentication: Secure token-based authentication with Bearer token validation
- API Gateway: Centralized routing and authentication filter
- Event-Driven Communication: Asynchronous messaging using RabbitMQ
- Multi-Module Maven Project: Organized with shared common module
- PostgreSQL Database: Relational data persistence for each service
- RESTful APIs: Standard REST endpoints with proper HTTP methods
- Java: 21
- Spring Boot: 3.2.0
- Spring Cloud Gateway: 2023.0.0
- Maven: Multi-module project structure
- Spring Security: Authentication and authorization
- Spring Data JPA: Database interaction
- Spring AMQP: RabbitMQ integration
- JWT (JJWT): 0.11.5 - JSON Web Token implementation
- PostgreSQL: Database
- Lombok: Code generation for POJOs
- Jackson: JSON serialization/deserialization
Purpose: API Gateway with JWT authentication filter
Responsibilities:
- Route requests to appropriate microservices
- Validate JWT tokens for protected endpoints
- Centralized security enforcement
Key Components:
JwtAuthenticationFilter: Custom gateway filter for JWT validationGatewayConfig: Route configuration for all services
Purpose: User management and authentication
Responsibilities:
- User registration and authentication
- JWT token generation
- Password encryption (BCrypt)
- Publish user registration events
Key Components:
AuthController: Registration and login endpointsUserService: Business logic for user operationsJwtUtil: JWT token generation and validationSecurityConfig: Spring Security configuration
Purpose: Order management
Responsibilities:
- Create and manage orders
- Publish order created events
- Listen to payment processed events
- Update order status based on payment results
Key Components:
OrderController: Order CRUD endpointsOrderService: Order business logicOrderEventPublisher: Publish order events to RabbitMQPaymentEventConsumer: Listen to payment events
Purpose: Payment processing and invoice generation
Responsibilities:
- Listen to order created events
- Process payments (simulated)
- Generate invoices
- Publish payment processed events
- Send welcome emails to new users
Key Components:
OrderEventConsumer: Listen to order eventsPaymentService: Payment processing logicInvoiceService: Invoice generationPaymentEventPublisher: Publish payment eventsWelcomeEmailListener: Listen to user registration events
Purpose: Shared data structures and utilities
Contains:
- DTOs (Data Transfer Objects):
UserDTO,OrderDTO,LoginRequest,LoginResponse - Events:
OrderCreatedEvent,PaymentProcessedEvent
microservices-jwt/
βββ pom.xml # Parent POM
βββ common/ # Shared module
β βββ src/main/java/com/common/
β β βββ dto/ # Data Transfer Objects
β β β βββ UserDTO.java
β β β βββ OrderDTO.java
β β β βββ LoginRequest.java
β β β βββ LoginResponse.java
β β βββ event/ # Event classes
β β βββ OrderCreatedEvent.java
β β βββ PaymentProcessedEvent.java
β βββ pom.xml
βββ gateway-service/
β βββ src/main/java/com/gateway_service/
β β βββ GatewayServiceApplication.java
β β βββ config/
β β β βββ GatewayConfig.java
β β βββ filter/
β β βββ JwtAuthenticationFilter.java
β βββ src/main/resources/
β β βββ application.properties
β βββ pom.xml
βββ user-service/
β βββ src/main/java/com/user_service/
β β βββ UserServiceApplication.java
β β βββ config/
β β β βββ JwtUtil.java
β β β βββ SecurityConfig.java
β β β βββ RabbitConfig.java
β β βββ controller/
β β β βββ AuthController.java
β β βββ model/
β β β βββ User.java
β β βββ repository/
β β β βββ UserRepository.java
β β βββ service/
β β βββ UserService.java
β βββ src/main/resources/
β β βββ application.properties
β βββ pom.xml
βββ order-service/
β βββ src/main/java/com/order_service/
β β βββ OrderServiceApplication.java
β β βββ config/
β β β βββ RabbitMQConfig.java
β β βββ controller/
β β β βββ OrderController.java
β β βββ model/
β β β βββ Order.java
β β βββ repository/
β β β βββ OrderRepository.java
β β βββ service/
β β βββ OrderService.java
β β βββ OrderEventPublisher.java
β β βββ PaymentEventConsumer.java
β βββ src/main/resources/
β β βββ application.properties
β βββ pom.xml
βββ payment-invoice-service/
βββ src/main/java/com/payment_invoice_service/
β βββ PaymentInvoiceServiceApplication.java
β βββ config/
β β βββ RabbitConfig.java
β βββ listener/
β β βββ WelcomeEmailListener.java
β βββ model/
β β βββ Payment.java
β β βββ Invoice.java
β βββ repository/
β β βββ PaymentRepository.java
β β βββ InvoiceRepository.java
β βββ service/
β βββ OrderEventConsumer.java
β βββ PaymentService.java
β βββ InvoiceService.java
β βββ PaymentEventPublisher.java
βββ src/main/resources/
β βββ application.properties
βββ pom.xml
users table:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL
);orders table:
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
product_name VARCHAR(255) NOT NULL,
quantity INTEGER NOT NULL,
amount DECIMAL(19,2) NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
created_at TIMESTAMP NOT NULL
);payments table:
CREATE TABLE payments (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
status VARCHAR(50) NOT NULL,
transaction_id VARCHAR(255),
processed_at TIMESTAMP NOT NULL
);invoices table:
CREATE TABLE invoices (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
user_email VARCHAR(255),
amount DECIMAL(19,2) NOT NULL,
product_name VARCHAR(255),
quantity INTEGER,
generated_at TIMESTAMP NOT NULL
);- Exchange:
user.exchange(TopicExchange) - Routing Key:
user.welcome - Queue:
user.welcome.queue - Purpose: Send welcome emails to newly registered users
- Queue:
order.queue - Purpose: Notify payment service when an order is created
- Event:
OrderCreatedEvent
- Queue:
payment.queue - Purpose: Update order status when payment is processed
- Event:
PaymentProcessedEvent
OrderCreatedEvent:
record OrderCreatedEvent(
Long orderId,
Long userId,
String productName,
Integer quantity,
BigDecimal amount
)PaymentProcessedEvent:
record PaymentProcessedEvent(
Long orderId,
Long userId,
String status,
String transactionId
)| Path Pattern | Target Service | Port | Authentication Required |
|---|---|---|---|
/api/auth/** |
user-service | 8081 | β No (Public) |
/api/orders/** |
order-service | 8083 | β Yes (JWT Required) |
Note: The gateway configuration shows commented routes for /api/users/** and /api/payments/** which can be enabled for additional secured endpoints.
Before running the application, ensure you have the following installed:
- Java Development Kit (JDK): Version 21 or higher
- Maven: Version 3.6 or higher
- PostgreSQL: Version 12 or higher
- RabbitMQ: Version 3.8 or higher
- IDE: IntelliJ IDEA, Eclipse, or VS Code (recommended)
git clone <repository-url>
cd microservices-jwtCreate a PostgreSQL database:
CREATE DATABASE user_db;Note: All services currently use the same database (user_db). In production, consider using separate databases for each service.
Ensure RabbitMQ is running:
# Start RabbitMQ server
rabbitmq-server
# Check RabbitMQ status
rabbitmqctl statusAccess RabbitMQ Management Console: http://localhost:15672
- Default credentials:
guest/guest
Navigate to the project root and build all modules:
mvn clean installOr build specific modules:
# Build common module first (required by others)
cd common
mvn clean install
# Build individual services
cd ../user-service
mvn clean package
cd ../gateway-service
mvn clean package
cd ../order-service
mvn clean package
cd ../payment-invoice-service
mvn clean packageserver.port=8081
spring.application.name=user-service
# Database
spring.datasource.url=jdbc:postgresql://localhost:5432/user_db
spring.datasource.username=postgres
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# JWT Configuration
jwt.secret=mySecretKeymySecretKeymySecretKeymySecretKeymySecretKeymySecretKey
jwt.expiration=86400000 # 24 hours in milliseconds
# RabbitMQ
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guestserver.port=8080
spring.application.name=gateway-service
# JWT Configuration (must match user-service)
jwt.secret=mySecretKeymySecretKeymySecretKeymySecretKeymySecretKeymySecretKey
spring.cloud.gateway.discovery.locator.enabled=falseserver.port=8083
spring.application.name=order-service
# Database
spring.datasource.url=jdbc:postgresql://localhost:5432/user_db
spring.datasource.username=postgres
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# RabbitMQ
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guestserver.port=8082
spring.application.name=payment-invoice-service
# Database
spring.datasource.url=jdbc:postgresql://localhost:5432/user_db
spring.datasource.username=postgres
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true- JWT secret must be identical in both
gateway-serviceanduser-service - Database credentials should match your PostgreSQL setup
- Change
jwt.secretin production to a secure value - Set
spring.jpa.hibernate.ddl-auto=updateorvalidatein production (notcreate-drop)
-
Start PostgreSQL and RabbitMQ (if not already running)
-
Start User Service:
cd user-service
mvn spring-boot:runService will start on: http://localhost:8081
- Start Gateway Service:
cd gateway-service
mvn spring-boot:runService will start on: http://localhost:8080
- Start Order Service:
cd order-service
mvn spring-boot:runService will start on: http://localhost:8083
- Start Payment-Invoice Service:
cd payment-invoice-service
mvn spring-boot:runService will start on: http://localhost:8082
# Build all services
mvn clean package
# Run services
java -jar user-service/target/user-service-0.0.1-SNAPSHOT.jar
java -jar gateway-service/target/gateway-service-0.0.1-SNAPSHOT.jar
java -jar order-service/target/order-service-0.0.1-SNAPSHOT.jar
java -jar payment-invoice-service/target/payment-invoice-service-0.0.1-SNAPSHOT.jarPOST http://localhost:8080/api/auth/register
Content-Type: application/json
{
"username": "john_doe",
"email": "john@example.com",
"password": "password123"
}Response:
{
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"password": null
}POST http://localhost:8080/api/auth/login
Content-Type: application/json
{
"username": "john_doe",
"password": "password123"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huX2RvZSIsImlhdCI6MTYzMjE...",
"username": "john_doe",
"email": "john@example.com"
}Note: All order endpoints require JWT authentication via Authorization: Bearer <token> header.
POST http://localhost:8080/api/orders
Content-Type: application/json
Authorization: Bearer <your-jwt-token>
{
"userId": 1,
"productName": "Laptop",
"quantity": 2,
"amount": 2500.00
}Response:
{
"id": 1,
"userId": 1,
"productName": "Laptop",
"quantity": 2,
"amount": 2500.00,
"status": "PENDING"
}GET http://localhost:8080/api/orders/user/1
Authorization: Bearer <your-jwt-token>Response:
[
{
"id": 1,
"userId": 1,
"productName": "Laptop",
"quantity": 2,
"amount": 2500.00,
"status": "PAID"
}
]GET http://localhost:8080/api/orders/1
Authorization: Bearer <your-jwt-token>For development/testing, you can access services directly:
- User Service:
http://localhost:8081/api/auth/** - Order Service:
http://localhost:8083/api/orders/**
Production: All requests should go through the API Gateway (port 8080).
1. User Registration
βββββββββββββββ
β Client β
ββββββββ¬βββββββ
β POST /api/auth/register
βΌ
ββββββββββββββββ
β Gateway β
ββββββββ¬ββββββββ
β
βΌ
ββββββββββββββββ Publish UserDTO ββββββββββββββββββββ
β User Service βββββββto user.welcomeββββββββ>β Payment-Invoice β
ββββββββββββββββ β Service β
β ββββββββββββββββββββ
β β
βΌ βΌ
Save to DB Log: "π© Welcome email sent"
2. User Login
βββββββββββββββ
β Client β
ββββββββ¬βββββββ
β POST /api/auth/login
βΌ
ββββββββββββββββ
β Gateway β
ββββββββ¬ββββββββ
β
βΌ
ββββββββββββββββ
β User Service βββββββ> Generate JWT Token
ββββββββ¬ββββββββ
β
βΌ
Return token to client
3. Create Order Flow
βββββββββββββββ
β Client β
ββββββββ¬βββββββ
β POST /api/orders + JWT
βΌ
ββββββββββββββββ
β Gateway βββββββ> Validate JWT
ββββββββ¬ββββββββ
β
βΌ
ββββββββββββββββ Publish OrderCreatedEvent ββββββββββββββββββββ
βOrder Service ββββββββto order.queueβββββββββββββββ>β Payment-Invoice β
ββββββββββββββββ β Service β
β ββββββββββ¬ββββββββββ
β β
βΌ βΌ
Save Order βββββββββββββββββββββββ
(Status: PENDING) β Process Payment β
β Generate Invoice β
βββββββββββ¬ββββββββββββ
β
β Publish PaymentProcessedEvent
β to payment.queue
βΌ
ββββββββββββββββ ββββββββββββββββββββ
βOrder Service β<ββββββββββββββββββββββββββββββββββ€ Payment-Invoice β
ββββββββ¬ββββββββ β Service β
β ββββββββββββββββββββ
βΌ
Update Order Status
(Status: PAID)
- Order Created β
OrderCreatedEventpublished toorder.queue - Payment Service consumes event from
order.queue - Payment Processed β Creates Payment & Invoice records
- PaymentProcessedEvent published to
payment.queue - Order Service consumes event from
payment.queue - Order Updated β Status changes from
PENDINGtoPAID
Header:
{
"alg": "HS256",
"typ": "JWT"
}Payload:
{
"sub": "username",
"iat": 1632123456,
"exp": 1632209856
}- User Registration: Password encrypted with BCrypt
- User Login: Credentials validated, JWT token generated
- Token Validation: Gateway validates token before routing
- Protected Endpoints: Require valid JWT in Authorization header
User Service:
- Public endpoints:
/api/auth/** - All other endpoints require authentication
- Stateless session management
- CSRF disabled (for REST API)
Gateway Service:
- JWT validation in
JwtAuthenticationFilter - Returns
401 Unauthorizedfor invalid/missing tokens - Routes without filter are public (e.g.,
/api/auth/**)
- BCrypt hashing with default strength (10 rounds)
- Passwords never returned in API responses
- Stored securely in database
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"email": "test@example.com",
"password": "password123"
}'curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'TOKEN="<your-jwt-token>"
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"userId": 1,
"productName": "Gaming Laptop",
"quantity": 1,
"amount": 1500.00
}'curl -X GET http://localhost:8080/api/orders/user/1 \
-H "Authorization: Bearer $TOKEN"- Import Collection: Create a new collection in Postman
- Set Environment Variables:
base_url:http://localhost:8080token: (will be set after login)
- Register & Login: Save token from login response
- Use Token: Add to Authorization header for protected endpoints
Access RabbitMQ Management UI: http://localhost:15672
Verify Queues:
order.queuepayment.queueuser.welcome.queue
Monitor:
- Message rates
- Queue depths
- Consumer connections
Each service logs important events:
User Service:
User registered: john_doe
Welcome email event published for: john@example.com
Order Service:
Order created event published: 1
Received payment processed event for order: 1
Order 1 status updated to PAID
Payment-Invoice Service:
Received order created event: 1
Payment processed for order: 1
Invoice generated for order: 1
π© Received welcome email event: john@example.com
Symptom: 401 Unauthorized from Gateway Causes:
- JWT secret mismatch between services
- Token expired
- Malformed token
Solution:
# Ensure identical JWT secrets in both services
# user-service/application.properties
jwt.secret=mySecretKeymySecretKeymySecretKeymySecretKeymySecretKeymySecretKey
# gateway-service/application.properties
jwt.secret=mySecretKeymySecretKeymySecretKeymySecretKeymySecretKeymySecretKeySymptom: Connection refused to localhost:5672 Solution:
# Check RabbitMQ status
rabbitmqctl status
# Start RabbitMQ
rabbitmq-server
# Verify credentials in application.properties
spring.rabbitmq.username=guest
spring.rabbitmq.password=guestSymptom: Unable to connect to PostgreSQL Solution:
# Verify PostgreSQL is running
pg_isready
# Check database exists
psql -U postgres -c "\l"
# Create database if missing
psql -U postgres -c "CREATE DATABASE user_db;"
# Verify credentials in application.properties
spring.datasource.username=postgres
spring.datasource.password=rootSymptom: Port 8080/8081/8082/8083 already in use Solution:
# Windows
netstat -ano | findstr :<PORT>
taskkill /PID <PID> /F
# Linux/Mac
lsof -i :<PORT>
kill -9 <PID>
# Or change port in application.properties
server.port=8085Symptom: Events published but not processed Checks:
- Verify queues exist in RabbitMQ Management UI
- Check consumer connections
- Review Jackson converter configuration
- Ensure message serialization format matches
Solution:
// Ensure Jackson2JsonMessageConverter is configured
@Bean
public Jackson2JsonMessageConverter jsonConverter() {
return new Jackson2JsonMessageConverter();
}Symptom: Cannot resolve dependency on common module Solution:
# Build and install common module first
cd common
mvn clean installSymptom: Tables not created automatically Solution:
# For development, use create-drop
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# For production, use update or validate
spring.jpa.hibernate.ddl-auto=updateEnable debug logging for troubleshooting:
# In application.properties
logging.level.org.springframework=DEBUG
logging.level.com.user_service=DEBUG
logging.level.org.springframework.amqp=DEBUG
logging.level.org.springframework.security=DEBUG- Uses Lombok for reduced boilerplate
- Java Records for immutable DTOs and Events
- Spring Boot 3.x with Jakarta EE
- RESTful API design principles
- All services share same database (for simplicity)
- Production should use separate databases per service
- Auto-schema generation enabled (development only)
- JWT secret should be environment variable in production
- HTTPS should be enabled in production
- Consider OAuth2/OIDC for production systems
- Implement rate limiting on Gateway
- Services are stateless and can be horizontally scaled
- RabbitMQ provides message persistence
- Consider implementing Circuit Breaker pattern
- Add service discovery (Eureka) for dynamic routing
- Implement service discovery with Eureka
- Add distributed tracing with Zipkin/Sleuth
- Implement circuit breaker with Resilience4j
- Add centralized configuration with Spring Cloud Config
- Implement API rate limiting
- Add comprehensive unit and integration tests
- Dockerize all services with docker-compose
- Implement proper logging with ELK stack
- Add API documentation with Swagger/OpenAPI
- Implement CORS configuration
- Add refresh token mechanism
- Implement proper email service integration
This project is for educational and demonstration purposes.
Contributions are welcome! Please follow standard Git workflow:
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
For questions or support, please contact the development team.
Built with β€οΈ using Spring Boot and RabbitMQ