A production-ready RESTful Job Search API built with Spring Boot 3, PostgreSQL, Redis, and Docker. Features JWT authentication, role-based access control, rate limiting, token blacklisting, and full Swagger documentation.
- β JWT-based stateless authentication
- β
Role-based access control (
ROLE_USER,ROLE_ADMIN) - β Job search with multi-filter support (keyword, location, job type)
- β JPA Specifications for dynamic query building
- β Pagination & sorting with validated bounds
- β Rate limiting per IP (5 requests/minute)
- β Redis-backed token blacklist with logout support
- β Structured error responses on all endpoints
- β Swagger / OpenAPI 3 documentation with Bearer auth
- β Multi-stage Dockerfile for optimized image size
- β All secrets managed via environment variables
| Layer | Technology |
|---|---|
| Language | Java 17 |
| Framework | Spring Boot 3.3.4 |
| Security | Spring Security 6 + JWT (jjwt 0.11.5) |
| Database | PostgreSQL 15 |
| Cache / Session | Redis (Lettuce) |
| ORM | Hibernate / Spring Data JPA |
| Mapping | MapStruct |
| Validation | Jakarta Bean Validation |
| Rate Limiting | Bucket4j |
| Documentation | SpringDoc OpenAPI (Swagger UI) |
| Containerization | Docker + Docker Compose |
| Build Tool | Maven |
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client / Swagger UI β
βββββββββββββββββββββββββ¬ββββββββββββββββββββββββββ
β
βββββββββββΌβββββββββββ
β RateLimitFilter β (Bucket4j, per IP)
βββββββββββ¬βββββββββββ
β
βββββββββββΌβββββββββββ
β JwtAuthentication β (Token validation + Blacklist check)
β Filter β
βββββββββββ¬βββββββββββ
β
βββββββββββββββΌββββββββββββββ
β Controllers β
β AuthController β
β JobController β
βββββββββββββββ¬ββββββββββββββ
β
βββββββββββββββΌββββββββββββββ
β Services β
β AuthService β
β JobServiceImpl β
β TokenBlacklistService β
ββββββββ¬βββββββββββ¬ββββββββββ
β β
ββββββββββΌββββ βββββΌβββββββββ
β PostgreSQL β β Redis β
β (JPA) β β (Blacklist)β
βββββββββββββββ ββββββββββββββ
- Docker & Docker Compose
- Java 17+ (only if running locally without Docker)
1. Clone the repository:
git clone https://github.com/your-username/jobportal-api.git
cd jobportal-api2. Create a .env file in the project root:
DB_USERNAME=postgres
DB_PASSWORD=yourpassword
JWT_SECRET_KEY=404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
JWT_EXPIRATION=86400000
REDIS_HOST=redis
REDIS_PORT=63793. Build and start all services:
docker compose up --build4. Open Swagger UI:
http://localhost:8080/swagger-ui/index.html
1. Start PostgreSQL and Redis locally, then set environment variables:
export DB_USERNAME=postgres
export DB_PASSWORD=yourpassword
export JWT_SECRET_KEY=404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
export JWT_EXPIRATION=86400000
export REDIS_HOST=localhost
export REDIS_PORT=63792. Run the application:
./mvnw spring-boot:run| Variable | Description | Example |
|---|---|---|
DB_USERNAME |
PostgreSQL username | postgres |
DB_PASSWORD |
PostgreSQL password | secret |
JWT_SECRET_KEY |
Base64-encoded HMAC secret (min 256-bit) | 404E63... |
JWT_EXPIRATION |
Token expiry in milliseconds | 86400000 (24h) |
REDIS_HOST |
Redis hostname | redis (Docker) / localhost |
REDIS_PORT |
Redis port | 6379 |
β οΈ Never commit the.envfile to version control. It is listed in.gitignore.
| Method | Endpoint | Access | Description |
|---|---|---|---|
POST |
/api/v1/auth/register |
Public | Register a new user |
POST |
/api/v1/auth/login |
Public | Login and receive JWT token |
POST |
/api/v1/auth/logout |
Authenticated | Invalidate current token |
| Method | Endpoint | Access | Description |
|---|---|---|---|
GET |
/api/v1/jobs/search |
Public | Search jobs with filters |
POST |
/api/v1/jobs/create |
ROLE_ADMIN |
Create a new job listing |
| Parameter | Type | Default | Description |
|---|---|---|---|
keyword |
String | β | Search in title and description |
location |
String | β | Filter by location |
jobType |
Enum | β | FULL_TIME, PART_TIME, REMOTE, CONTRACT |
page |
Integer | 0 |
Page number (min: 0) |
size |
Integer | 10 |
Page size (min: 1, max: 100) |
sortBy |
String | createdAt |
createdAt, salary, title, companyName |
1. POST /api/v1/auth/register β 201 Created + JWT token
2. POST /api/v1/auth/login β 200 OK + JWT token
3. Use token in header: Authorization: Bearer <token>
4. POST /api/v1/auth/logout β 204 No Content (token blacklisted in Redis)
Register request body:
{
"firstname": "John",
"lastname": "Doe",
"email": "john@example.com",
"password": "secret123"
}Login request body:
{
"email": "john@example.com",
"password": "secret123"
}Response:
{
"accessToken": "eyJhbGci...",
"tokenType": "Bearer"
}Requests are rate-limited per IP address using Bucket4j:
- Limit: 5 requests per minute
- Scope: All endpoints except
/api/v1/auth/**and Swagger UI - Exceeded response:
429 Too Many Requests
| Feature | Implementation |
|---|---|
| Password hashing | BCrypt |
| Token format | JWT (HS256) |
| Session policy | Stateless |
| Token revocation | Redis blacklist (TTL = token expiry) |
| Role enforcement | @PreAuthorize + @EnableMethodSecurity |
| CORS | Configurable origin allowlist |
| Secret management | Environment variables only β no hardcoded credentials |
src/main/java/com/example/
βββ config/
β βββ AppConfig.java # AuthProvider, PasswordEncoder, Swagger config
β βββ DataInitializer.java # Seeds ROLE_USER and ROLE_ADMIN on startup
βββ controller/
β βββ AuthController.java # Register, Login, Logout
β βββ JobController.java # Search, Create
βββ dto/
β βββ AuthResponse.java
β βββ JobCreateDto.java
β βββ JobResponseDto.java
β βββ JobSearchRequestDto.java
β βββ LoginRequest.java
β βββ RegisterRequest.java
βββ entity/
β βββ Job.java
β βββ Role.java
β βββ User.java
βββ enums/
β βββ JobType.java
β βββ RoleType.java
βββ exception/
β βββ ErrorDetails.java
β βββ GlobalExceptionHandler.java
β βββ ResourceNotFoundException.java
β βββ UserNotFoundException.java
βββ filter/
β βββ RateLimitFilter.java # Bucket4j IP-based rate limiting
βββ mapper/
β βββ JobMapper.java # MapStruct mapper
βββ repository/
β βββ JobRepository.java
β βββ RoleRepository.java
β βββ UserRepository.java
β βββ specification/
β βββ JobSpecification.java # Dynamic JPA Specifications
βββ security/
β βββ CustomUserDetailsService.java
β βββ JwtAuthenticationFilter.java
β βββ JwtService.java
β βββ SecurityConfig.java
βββ service/
βββ AuthService.java
βββ JobService.java # Interface
βββ TokenBlacklistService.java # Redis-backed token blacklist
βββ impls/
βββ JobServiceImpl.java
This project is for educational purposes.