A secure Spring Boot REST API implementing JWT (JSON Web Token) authentication with refresh token mechanism. The project demonstrates layered architecture, Spring Security integration, BCrypt password encoding, and protected endpoints for employee management.
This API provides secure authentication and authorization using JWT tokens:
- User Registration: Create new user accounts with encrypted passwords
- User Authentication: Login with username/password to receive JWT access token and refresh token
- Token Refresh: Obtain new access tokens using refresh tokens without re-authentication
- Protected Resources: Access employee data with valid JWT tokens
- API Documentation: Swagger/OpenAPI integration for interactive API testing
Layered architecture following best practices:
entities/ - JPA entities (User, Employee, Department, RefreshToken)
dao/ - Spring Data JPA repositories
business/ - Service interfaces and implementations
api/ - REST controllers
dto/ - Data Transfer Objects for request/response
config/ - Spring configuration (Security, App, Swagger)
jwt/ - JWT service, filter, and authentication components
- JwtService: Token generation, validation, and parsing
- JwtAuthenticationFilter: Custom filter for JWT validation on each request
- AuthManager: Authentication and registration business logic
- RefreshTokenManager: Refresh token generation and validation
- SecurityConfig: Spring Security configuration with JWT filter chain
- AppConfig: UserDetailsService, AuthenticationProvider, PasswordEncoder beans
- Java 21
- Spring Boot
- Spring Security - Authentication and authorization
- Spring Data JPA - Database access
- PostgreSQL - Relational database
- JWT - Token generation and validation
- BCrypt - Password hashing
- Lombok - Boilerplate code reduction
- Swagger/OpenAPI - API documentation
- Bean Validation - Request validation
- JDK 21
- PostgreSQL
- Maven (or use Maven Wrapper)
Configure PostgreSQL connection in src/main/resources/application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/jwt
spring.datasource.username=postgres
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.show-sql=trueUsing Maven Wrapper:
# Linux/Mac
./mvnw spring-boot:run
# Windows
mvnw.cmd spring-boot:runUsing Maven:
mvn clean install
mvn spring-boot:runThe application will start on http://localhost:8080
- Access Token Expiration: 2 hours
- Refresh Token Expiration: 4 hours
- Secret Key: Base64 encoded (configured in
JwtService.SECRET_KEY) - Algorithm: HS256
- Public Endpoints:
/register,/authenticate,/refreshToken, Swagger UI paths - Protected Endpoints: All other endpoints require valid JWT token
- Session Management: Stateless (JWT-based)
- CSRF: Disabled (REST API)
- Fields:
id,userName,password - Implements:
UserDetails(Spring Security) - Relationships: OneToMany with RefreshToken
- Fields:
id,firstName,lastName - Relationships: ManyToOne with Department
- Fields:
id,name,location - Relationships: OneToMany with Employee
- Fields:
id,refreshToken(UUID),expireDate - Relationships: ManyToOne with User
AuthRequest:
username(required, @NotEmpty)password(required, @NotEmpty)
RefreshTokenRequest:
refreshToken(required)
AuthResponse:
accessToken(JWT token)refreshToken(UUID string)
DtoUser:
id,userName(password excluded)
DtoEmployee:
id,firstName,lastName,department(DtoDepartment)
DtoDepartment:
id,name,location
-
Registration:
- Password is encrypted using BCrypt before saving
- Username must be unique
-
Login:
- Username and password validated against database
- On success: Access token (2h) + Refresh token (4h) generated
- Refresh token saved to database
-
Token Refresh:
- Refresh token validated (exists and not expired)
- New access token generated
- New refresh token generated and saved (old one replaced)
- All passwords hashed with BCrypt
- JWT tokens signed with HS256 algorithm
- Token expiration enforced
- Stateless session management
- 401 Unauthorized: Invalid or missing JWT token
- 403 Forbidden: Valid token but insufficient permissions
- 400 Bad Request: Validation errors
- 500 Internal Server Error: Server-side errors
POST /register
Content-Type: application/json
Request Body:
{
"username": "john_doe",
"password": "securePassword123"
}
Response: 200 OK
{
"id": 1,
"userName": "john_doe"
}
POST /authenticate
Content-Type: application/json
Request Body:
{
"username": "john_doe",
"password": "securePassword123"
}
Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440000"
}
POST /refreshToken
Content-Type: application/json
Request Body:
{
"refreshToken": "550e8400-e29b-41d4-a716-446655440000"
}
Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "new-refresh-token-uuid"
}
GET /employees/{id}
Authorization: Bearer {accessToken}
Response: 200 OK
{
"id": 1,
"firstName": "John",
"lastName": "Doe",
"department": {
"id": 1,
"name": "Engineering",
"location": "Istanbul"
}
}
curl -X POST http://localhost:8080/register \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'curl -X POST http://localhost:8080/authenticate \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'Save the accessToken and refreshToken from the response.
curl -X GET http://localhost:8080/employees/1 \
-H "Authorization: Bearer {accessToken}"When access token expires, use refresh token:
curl -X POST http://localhost:8080/refreshToken \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "{refreshToken}"
}'Once the application is running, access Swagger UI at:
http://localhost:8080/swagger-ui.html
Or:
http://localhost:8080/swagger-ui/index.html
API documentation is available at:
http://localhost:8080/v3/api-docs
Note: Swagger UI allows you to test endpoints directly. Click "Authorize" button and enter your JWT token in the format: Bearer {your-token}
- Password Encryption: BCrypt hashing
- JWT Tokens: Stateless authentication
- Token Expiration: Access tokens expire after 2 hours
- Refresh Tokens: Long-lived tokens (4 hours) for seamless re-authentication
- Filter Chain: Custom JWT filter validates tokens on every request
- UserDetails Integration: Spring Security UserDetails implementation
src/main/java/com/example/
├── api/
│ ├── impl/
│ │ ├── RestAuthControllerImpl.java
│ │ └── RestEmployeeControllerImpl.java
│ ├── IRestAuthController.java
│ └── IRestEmployeeController.java
├── business/
│ ├── abstracts/
│ │ ├── IAuthService.java
│ │ ├── IEmployeeService.java
│ │ └── IRefreshTokenService.java
│ └── concretes/
│ ├── AuthManager.java
│ ├── EmployeeManager.java
│ └── RefreshTokenManager.java
├── config/
│ ├── AppConfig.java
│ ├── SecurityConfig.java
│ └── SwaggerConfig.java
├── dao/
│ ├── EmployeeRepo.java
│ ├── RefreshTokenRepo.java
│ └── UserRepo.java
├── dto/
│ ├── DtoDepartment.java
│ ├── DtoEmployee.java
│ └── DtoUser.java
├── entities/
│ ├── Department.java
│ ├── Employee.java
│ ├── RefreshToken.java
│ └── User.java
├── jwt/
│ ├── AuthEntryPoint.java
│ ├── AuthRequest.java
│ ├── AuthResponse.java
│ ├── JwtAuthenticationFilter.java
│ ├── JwtService.java
│ └── RefreshTokenRequest.java
└── JwtApplication.java
- Secret Key: In production, move
SECRET_KEYfrom code to environment variables or secure configuration - Token Storage: Refresh tokens are stored in database; consider cleanup strategy for expired tokens
- UserDetails Methods: User entity implements all required UserDetails methods (
isEnabled,isAccountNonExpired, etc.) - Repository Method Naming:
findByUserNamematches entity field name (userName), notfindByUsername
- Ensure JWT token is included in
Authorizationheader:Bearer {token} - Check token expiration
- Verify token format (should start with
Bearer)
- Invalid username/password
- Token expired (use refresh token endpoint)
- Missing or malformed token
- Verify PostgreSQL is running
- Check database credentials in
application.properties - Ensure database
jwtexists
JWT (JSON Web Token) kimlik doğrulama ve refresh token mekanizması içeren güvenli bir Spring Boot REST API. Proje, katmanlı mimari, Spring Security entegrasyonu, BCrypt şifre şifreleme ve çalışan yönetimi için korumalı endpoint'leri gösterir.
Bu API, JWT token'ları kullanarak güvenli kimlik doğrulama ve yetkilendirme sağlar:
- Kullanıcı Kaydı: Şifrelenmiş şifrelerle yeni kullanıcı hesapları oluşturma
- Kullanıcı Kimlik Doğrulama: Kullanıcı adı/şifre ile giriş yaparak JWT access token ve refresh token alma
- Token Yenileme: Yeniden kimlik doğrulama yapmadan refresh token kullanarak yeni access token alma
- Korumalı Kaynaklar: Geçerli JWT token'ları ile çalışan verilerine erişim
- API Dokümantasyonu: İnteraktif API testi için Swagger/OpenAPI entegrasyonu
En iyi uygulamaları takip eden katmanlı mimari:
entities/ - JPA varlıkları (User, Employee, Department, RefreshToken)
dao/ - Spring Data JPA repository'leri
business/ - Servis arayüzleri ve implementasyonları
api/ - REST controller'lar
dto/ - İstek/yanıt için Veri Transfer Nesneleri
config/ - Spring yapılandırması (Security, App, Swagger)
jwt/ - JWT servisi, filtre ve kimlik doğrulama bileşenleri
- JwtService: Token üretimi, doğrulama ve ayrıştırma
- JwtAuthenticationFilter: Her istekte JWT doğrulaması için özel filtre
- AuthManager: Kimlik doğrulama ve kayıt iş mantığı
- RefreshTokenManager: Refresh token üretimi ve doğrulama
- SecurityConfig: JWT filtre zinciri ile Spring Security yapılandırması
- AppConfig: UserDetailsService, AuthenticationProvider, PasswordEncoder bean'leri
- Java 21
- Spring Boot
- Spring Security - Kimlik doğrulama ve yetkilendirme
- Spring Data JPA - Veritabanı erişimi
- PostgreSQL - İlişkisel veritabanı
- JWT - Token üretimi ve doğrulama
- BCrypt - Şifre hash'leme
- Lombok - Tekrarlayan kod azaltma
- Swagger/OpenAPI - API dokümantasyonu
- Bean Validation - İstek doğrulama
- JDK 21
- PostgreSQL
- Maven (veya Maven Wrapper kullanın)
PostgreSQL bağlantısını src/main/resources/application.properties dosyasında yapılandırın:
spring.datasource.url=jdbc:postgresql://localhost:5432/jwt
spring.datasource.username=postgres
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.show-sql=trueMaven Wrapper Kullanarak:
# Linux/Mac
./mvnw spring-boot:run
# Windows
mvnw.cmd spring-boot:runMaven Kullanarak:
mvn clean install
mvn spring-boot:runUygulama http://localhost:8080 adresinde başlayacaktır.
- Access Token Süresi: 2 saat
- Refresh Token Süresi: 4 saat
- Gizli Anahtar: Base64 kodlanmış (
JwtService.SECRET_KEYiçinde yapılandırılmış) - Algoritma: HS256
- Herkese Açık Endpoint'ler:
/register,/authenticate,/refreshToken, Swagger UI yolları - Korumalı Endpoint'ler: Diğer tüm endpoint'ler geçerli JWT token gerektirir
- Oturum Yönetimi: Durumsuz (JWT tabanlı)
- CSRF: Devre dışı (REST API)
- Alanlar:
id,userName,password - Uygular:
UserDetails(Spring Security) - İlişkiler: RefreshToken ile OneToMany
- Alanlar:
id,firstName,lastName - İlişkiler: Department ile ManyToOne
- Alanlar:
id,name,location - İlişkiler: Employee ile OneToMany
- Alanlar:
id,refreshToken(UUID),expireDate - İlişkiler: User ile ManyToOne
AuthRequest:
username(zorunlu, @NotEmpty)password(zorunlu, @NotEmpty)
RefreshTokenRequest:
refreshToken(zorunlu)
AuthResponse:
accessToken(JWT token)refreshToken(UUID string)
DtoUser:
id,userName(şifre hariç)
DtoEmployee:
id,firstName,lastName,department(DtoDepartment)
DtoDepartment:
id,name,location
-
Kayıt:
- Şifre kaydedilmeden önce BCrypt ile şifrelenir
- Kullanıcı adı benzersiz olmalıdır
-
Giriş:
- Kullanıcı adı ve şifre veritabanına karşı doğrulanır
- Başarılı olursa: Access token (2s) + Refresh token (4s) üretilir
- Refresh token veritabanına kaydedilir
-
Token Yenileme:
- Refresh token doğrulanır (mevcut ve süresi dolmamış)
- Yeni access token üretilir
- Yeni refresh token üretilir ve kaydedilir (eskisi değiştirilir)
- Tüm şifreler BCrypt ile hash'lenir
- JWT token'ları HS256 algoritması ile imzalanır
- Token süresi zorunludur
- Durumsuz oturum yönetimi
- 401 Unauthorized: Geçersiz veya eksik JWT token
- 403 Forbidden: Geçerli token ancak yetersiz yetkiler
- 400 Bad Request: Doğrulama hataları
- 500 Internal Server Error: Sunucu tarafı hatalar
POST /register
Content-Type: application/json
İstek Gövdesi:
{
"username": "john_doe",
"password": "securePassword123"
}
Yanıt: 200 OK
{
"id": 1,
"userName": "john_doe"
}
POST /authenticate
Content-Type: application/json
İstek Gövdesi:
{
"username": "john_doe",
"password": "securePassword123"
}
Yanıt: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440000"
}
POST /refreshToken
Content-Type: application/json
İstek Gövdesi:
{
"refreshToken": "550e8400-e29b-41d4-a716-446655440000"
}
Yanıt: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "new-refresh-token-uuid"
}
GET /employees/{id}
Authorization: Bearer {accessToken}
Yanıt: 200 OK
{
"id": 1,
"firstName": "John",
"lastName": "Doe",
"department": {
"id": 1,
"name": "Engineering",
"location": "Istanbul"
}
}
curl -X POST http://localhost:8080/register \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'curl -X POST http://localhost:8080/authenticate \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'Yanıttan accessToken ve refreshToken değerlerini kaydedin.
curl -X GET http://localhost:8080/employees/1 \
-H "Authorization: Bearer {accessToken}"Access token süresi dolduğunda, refresh token kullanın:
curl -X POST http://localhost:8080/refreshToken \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "{refreshToken}"
}'Uygulama çalıştıktan sonra, Swagger UI'ya şu adresten erişin:
http://localhost:8080/swagger-ui.html
Veya:
http://localhost:8080/swagger-ui/index.html
API dokümantasyonu şu adreste mevcuttur:
http://localhost:8080/v3/api-docs
Not: Swagger UI, endpoint'leri doğrudan test etmenize olanak tanır. "Authorize" düğmesine tıklayın ve JWT token'ınızı şu formatta girin: Bearer {your-token}
- Şifre Şifreleme: BCrypt hash'leme
- JWT Token'ları: Durumsuz kimlik doğrulama
- Token Süresi: Access token'lar 2 saat sonra sona erer
- Refresh Token'lar: Sorunsuz yeniden kimlik doğrulama için uzun ömürlü token'lar (4 saat)
- Filtre Zinciri: Özel JWT filtresi her istekte token'ları doğrular
- UserDetails Entegrasyonu: Spring Security UserDetails implementasyonu
src/main/java/com/example/
├── api/
│ ├── impl/
│ │ ├── RestAuthControllerImpl.java
│ │ └── RestEmployeeControllerImpl.java
│ ├── IRestAuthController.java
│ └── IRestEmployeeController.java
├── business/
│ ├── abstracts/
│ │ ├── IAuthService.java
│ │ ├── IEmployeeService.java
│ │ └── IRefreshTokenService.java
│ └── concretes/
│ ├── AuthManager.java
│ ├── EmployeeManager.java
│ └── RefreshTokenManager.java
├── config/
│ ├── AppConfig.java
│ ├── SecurityConfig.java
│ └── SwaggerConfig.java
├── dao/
│ ├── EmployeeRepo.java
│ ├── RefreshTokenRepo.java
│ └── UserRepo.java
├── dto/
│ ├── DtoDepartment.java
│ ├── DtoEmployee.java
│ └── DtoUser.java
├── entities/
│ ├── Department.java
│ ├── Employee.java
│ ├── RefreshToken.java
│ └── User.java
├── jwt/
│ ├── AuthEntryPoint.java
│ ├── AuthRequest.java
│ ├── AuthResponse.java
│ ├── JwtAuthenticationFilter.java
│ ├── JwtService.java
│ └── RefreshTokenRequest.java
└── JwtApplication.java
- Gizli Anahtar: Üretimde,
SECRET_KEY'i kod dışına, ortam değişkenlerine veya güvenli yapılandırmaya taşıyın - Token Depolama: Refresh token'lar veritabanında saklanır; süresi dolmuş token'lar için temizleme stratejisi düşünün
- UserDetails Metodları: User varlığı tüm gerekli UserDetails metodlarını uygular (
isEnabled,isAccountNonExpired, vb.) - Repository Metod İsimlendirme:
findByUserNameentity alan adıyla eşleşir (userName),findByUsernamedeğil
- JWT token'ın
Authorizationbaşlığında olduğundan emin olun:Bearer {token} - Token süresini kontrol edin
- Token formatını doğrulayın (
Bearerile başlamalı)
- Geçersiz kullanıcı adı/şifre
- Token süresi dolmuş (refresh token endpoint'ini kullanın)
- Eksik veya hatalı biçimlendirilmiş token
- PostgreSQL'in çalıştığını doğrulayın
application.propertiesdosyasındaki veritabanı kimlik bilgilerini kontrol edinjwtveritabanının mevcut olduğundan emin olun