ระบบ RESTful API สำหรับ Authentication และ Authorization พัฒนาด้วย Spring Boot, Spring Security, JWT และ PostgreSQL โดยออกแบบเป็น Monolithic Architecture และแยกความรับผิดชอบของแต่ละ Layer อย่างชัดเจน เพื่อให้ง่ายต่อการพัฒนา ทดสอบ และบำรุงรักษา
- Spring Boot Authentication & Authorization API
โปรเจกต์นี้มีวัตถุประสงค์เพื่อสร้างระบบ Authentication และ Authorization สำหรับ RESTful API โดยใช้ JWT สำหรับยืนยันตัวตน และ Spring Security สำหรับควบคุมการเข้าถึงทรัพยากรตามสิทธิ์ของผู้ใช้
ระบบจัดเก็บข้อมูลผู้ใช้ บทบาท สิทธิ์การใช้งาน Session และประวัติการเข้าสู่ระบบใน PostgreSQL
- Architecture: Monolithic
- Application Type: RESTful API
- Authentication: JWT
- Authorization: Role-Based Access Control (RBAC)
- Database: PostgreSQL
- ORM: Spring Data JPA
| Technology | Purpose |
|---|---|
| Java | Programming Language |
| Spring Boot | Application Framework |
| Spring Web | REST API Development |
| Spring Security | Authentication and Authorization |
| JWT | Token-Based Authentication |
| Spring Data JPA | Database Access |
| PostgreSQL | Relational Database |
| Lombok | Reduce Boilerplate Code |
| Bean Validation | Request Validation |
| Maven | Dependency Management |
| JUnit 5 | Unit Testing |
| Mockito | Mocking Dependencies |
เวอร์ชันของ Java, Spring Boot และ JWT Library ให้ยึดตามเวอร์ชันที่กำหนดไว้ในโปรเจกต์จริง
ระบบใช้ Layered Architecture เพื่อแยกความรับผิดชอบของแต่ละส่วน
Client
|
v
Controller Layer
|
v
Security Layer
|
v
Service Layer
|
v
Repository Layer
|
v
PostgreSQL
| Layer | Responsibility |
|---|---|
| Controller | รับ HTTP Request และส่ง HTTP Response |
| Security | ตรวจสอบ JWT และควบคุมสิทธิ์การเข้าถึง |
| Service | ประมวลผล Business Logic |
| Repository | ติดต่อฐานข้อมูลผ่าน Spring Data JPA |
| Entity | Mapping ระหว่าง Java Object และ Database Table |
| DTO | กำหนดโครงสร้าง Request และ Response |
| Exception | จัดการข้อผิดพลาดของระบบ |
| Common | จัดเก็บองค์ประกอบที่ใช้ร่วมกัน |
src/
└── main/
├── java/com/example/auth/
│ ├── AuthApplication.java
│ │
│ ├── config/
│ │ ├── SecurityConfig.java
│ │ ├── JpaConfig.java
│ │ └── OpenApiConfig.java
│ │
│ ├── security/
│ │ ├── JwtAuthenticationFilter.java
│ │ ├── JwtAuthenticationEntryPoint.java
│ │ ├── CustomAccessDeniedHandler.java
│ │ ├── CustomUserDetails.java
│ │ └── CustomUserDetailsService.java
│ │
│ ├── auth/
│ │ ├── AuthController.java
│ │ ├── AuthService.java
│ │ ├── JwtService.java
│ │ └── dto/
│ │ ├── LoginRequest.java
│ │ ├── RefreshTokenRequest.java
│ │ └── LoginResponse.java
│ │
│ ├── user/
│ │ ├── UserController.java
│ │ ├── UserService.java
│ │ ├── UserRepository.java
│ │ ├── User.java
│ │ └── dto/
│ │ ├── CreateUserRequest.java
│ │ ├── UpdateUserRequest.java
│ │ └── UserResponse.java
│ │
│ ├── role/
│ │ ├── RoleController.java
│ │ ├── RoleService.java
│ │ ├── RoleRepository.java
│ │ └── Role.java
│ │
│ ├── userrole/
│ │ ├── UserRoleService.java
│ │ ├── UserRoleRepository.java
│ │ └── UserRole.java
│ │
│ ├── session/
│ │ ├── SessionController.java
│ │ ├── SessionService.java
│ │ ├── SessionRepository.java
│ │ ├── UserSession.java
│ │ └── dto/
│ │ └── SessionResponse.java
│ │
│ ├── token/
│ │ ├── TokenMonitorController.java
│ │ ├── TokenMonitorService.java
│ │ └── dto/
│ │ └── TokenMonitorResponse.java
│ │
│ ├── loginlog/
│ │ ├── LoginLogController.java
│ │ ├── LoginLogService.java
│ │ ├── LoginLogRepository.java
│ │ ├── LoginLog.java
│ │ └── dto/
│ │ ├── LoginLogFilterRequest.java
│ │ └── LoginLogResponse.java
│ │
│ ├── common/
│ │ ├── exception/
│ │ │ └── GlobalExceptionHandler.java
│ │ ├── response/
│ │ │ └── ApiResponse.java
│ │ └── enums/
│ │ ├── UserStatus.java
│ │ ├── LoginEventType.java
│ │ └── SessionStatus.java
│ │
│ └── audit/
│ ├── AuditLogService.java
│ └── AuditEvent.java
│
└── resources/
├── application.yml
├── application-dev.yml
└── db/
└── migration/
src/test/
└── java/com/example/auth/
โครงสร้างนี้เป็นโครงสร้างเป้าหมายสำหรับการพัฒนา ไม่ได้หมายความว่าทุกคลาสถูกสร้างและ Implement แล้ว
- Login ด้วย Username และ Password
- ตรวจสอบ Password ด้วย Password Encoder
- สร้างและตรวจสอบ JWT
- โหลดข้อมูลผู้ใช้ผ่าน Spring Security
- รองรับการกำหนดอายุ Token
- รองรับ Refresh Token เมื่อพัฒนากลไกดังกล่าว
- รองรับ Logout และ Session Revocation เมื่อมีการ Implement
- กำหนดสิทธิ์ด้วย Role-Based Access Control (RBAC)
- จำกัดการเข้าถึง Endpoint ตาม Role
- รองรับการเพิ่ม Role และ Permission ในอนาคต
- บันทึกประวัติ Login สำเร็จและล้มเหลว
- จัดเก็บวันและเวลาของเหตุการณ์
- จัดเก็บ IP Address และ User-Agent ตามความเหมาะสม
- ติดตาม Session และเวลาหมดอายุ
- รองรับการตรวจสอบประวัติการเข้าสู่ระบบ
ฐานข้อมูลหลักของระบบคือ auth_db บน PostgreSQL
ตารางที่ระบบอาจใช้ประกอบด้วย:
| Table | Purpose |
|---|---|
users |
จัดเก็บข้อมูลผู้ใช้ |
roles |
จัดเก็บบทบาทของผู้ใช้ |
user_roles |
เชื่อมความสัมพันธ์ระหว่างผู้ใช้และบทบาท หากออกแบบเป็น Many-to-Many |
sessions |
จัดเก็บข้อมูล Session หรือ Token Lifecycle |
login_logs |
จัดเก็บประวัติการเข้าสู่ระบบ |
ชื่อตารางข้างต้นเป็นชื่ออ้างอิง ต้องปรับให้ตรงกับ SQL Schema ที่สร้างไว้จริง
ตารางที่ต้องการตรวจสอบการเปลี่ยนแปลงควรมีฟิลด์วันที่ตาม Schema ที่กำหนด เช่น
created_date— วันที่สร้างข้อมูลupdated_date— วันที่แก้ไขข้อมูลล่าสุด
สามารถใช้ Spring Data JPA Auditing เพื่อจัดการวันที่อัตโนมัติ โดยต้องกำหนด Annotation และเปิดใช้งาน Auditing ให้ถูกต้อง
ก่อนเริ่มใช้งาน ควรติดตั้ง:
- JDK เวอร์ชันที่รองรับกับโปรเจกต์
- Maven หรือ Maven Wrapper
- PostgreSQL
- Git
- IDE เช่น IntelliJ IDEA หรือ Visual Studio Code
กำหนดค่า Database และ JWT ผ่าน Environment Variables แทนการเก็บ Secret ไว้ใน Source Code
ตัวอย่าง application.yml:
spring:
application:
name: auth-api
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
open-in-view: false
properties:
hibernate:
format_sql: true
security:
jwt:
secret: ${JWT_SECRET}
access-token-expiration: ${JWT_ACCESS_EXPIRATION_MS:900000}ตัวอย่าง Environment Variables สำหรับเครื่องพัฒนา:
export DB_URL="jdbc:postgresql://localhost:5432/auth_db"
export DB_USERNAME="your_db_username"
export DB_PASSWORD="your_db_password"
export JWT_SECRET="your_base64_encoded_secret"
export JWT_ACCESS_EXPIRATION_MS="900000"ข้อควรระวัง
JWT_SECRETต้องมีความแข็งแรงและมีขนาดเหมาะสมกับอัลกอริทึมที่เลือก- ห้าม Commit Password และ JWT Secret ลง Git
900000มิลลิวินาที เท่ากับ 15 นาทีddl-auto: validateใช้ตรวจสอบ Schema ที่มีอยู่ ไม่ได้สร้างหรือแก้ไขตารางให้โดยอัตโนมัติ- ต้องตรวจสอบว่า JWT Library อ่านค่า Secret ในรูปแบบเดียวกับที่กำหนด
git clone <YOUR_REPOSITORY_URL>
cd <YOUR_PROJECT_DIRECTORY>สร้าง Database ใน PostgreSQL หากยังไม่มี:
CREATE DATABASE auth_db;จากนั้นรัน SQL Schema ที่ออกแบบไว้เพื่อสร้างตารางที่จำเป็น
หากมีตารางอยู่แล้ว ให้ตรวจสอบชื่อ Column, Primary Key, Foreign Key และชนิดข้อมูลให้ตรงกับ Entity
กำหนด Environment Variables ตามหัวข้อ Configuration ให้ครบ
./mvnw clean packageหากใช้ Maven ที่ติดตั้งในเครื่อง:
mvn clean package./mvnw spring-boot:runหรือรันไฟล์ JAR:
java -jar target/<APPLICATION_JAR_NAME>.jarตรวจสอบว่า Application เริ่มทำงานสำเร็จ และเชื่อมต่อ PostgreSQL ได้
Client
|
| POST /api/auth/login
v
AuthController
|
v
AuthService
|
+--> UserRepository
| Find User
|
+--> PasswordEncoder
| Verify Password
|
+--> LoginLogService
| Record Login Result
|
+--> JwtService
| Generate JWT
|
+--> SessionService
Record Session if enabled
|
v
Return Access Token
|
v
Client sends Authorization: Bearer <token>
|
v
JwtAuthenticationFilter
|
+--> Validate Token
|
+--> Set SecurityContext
|
v
Protected Controller
Endpoint ที่แนะนำสำหรับระบบ Authentication มีดังนี้
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/login |
เข้าสู่ระบบ |
| POST | /api/auth/refresh |
ขอ Access Token ใหม่ หากเปิดใช้ Refresh Token |
| POST | /api/auth/logout |
ออกจากระบบและเพิกถอน Session ตามที่ออกแบบ |
| GET | /api/auth/me |
ดูข้อมูลผู้ใช้ที่เข้าสู่ระบบ |
| GET | /api/users |
ดูรายชื่อผู้ใช้ตามสิทธิ์ |
| GET | /api/admin/logs |
ตรวจสอบ Login Logs ตามสิทธิ์ที่กำหนด |
รายการนี้เป็น API ที่แนะนำ ไม่ใช่การยืนยันว่า Endpoint ทั้งหมดมีอยู่แล้วในโปรเจกต์
POST /api/auth/login
Content-Type: application/json{
"username": "example_user",
"password": "your_password"
}GET /api/auth/me
Authorization: Bearer <ACCESS_TOKEN>โครงสร้าง JSON Response จริงควรอ้างอิง DTO ที่ Implement ไว้ในโปรเจกต์
แนวทางด้านความปลอดภัยที่ควรปฏิบัติ:
- ใช้
PasswordEncoderเช่น BCrypt สำหรับ Hash Password - ตรวจสอบ Signature, Expiration และ Claims ที่จำเป็นของ JWT
- ใช้ HTTPS ใน Production
- กำหนด Endpoint แบบ Public เฉพาะที่จำเป็น
- ตรวจสอบ Role และ Permission ที่ฝั่ง Server เสมอ
- ไม่เก็บ Password หรือ JWT แบบเต็มใน Log
- จำกัดสิทธิ์การเข้าถึง Login Logs
- ใช้ Secret ผ่าน Environment Variables หรือ Secret Manager
- กำหนด Rate Limit หรือกลไกป้องกัน Brute-Force สำหรับ Login
- ออกแบบการเพิกถอน Token และ Session ให้ชัดเจน
สำหรับระบบ JWT แบบ Stateless การ Logout ฝั่ง Client เพียงอย่างเดียวไม่ได้ทำให้ Token ที่ยังไม่หมดอายุใช้ไม่ได้ทันที หากต้องการเพิกถอน Token ต้องมีกลไกฝั่ง Server รองรับ
แยก Application Log ออกจาก Login Audit Log ให้ชัดเจน
ใช้ติดตามการทำงานและข้อผิดพลาดของ Application เช่น:
- Application Startup
- Database Connection Error
- Unexpected Exception
- Integration Error
ใช้บันทึกเหตุการณ์ที่เกี่ยวข้องกับการเข้าสู่ระบบ เช่น:
- Login Success
- Login Failure
- Logout
- Token หรือ Session Revocation
ควรบันทึกเฉพาะข้อมูลที่จำเป็นต่อการตรวจสอบ และหลีกเลี่ยงการเก็บข้อมูลลับหรือข้อมูลส่วนบุคคลเกินความจำเป็น
ควรมี Unit Tests และ Integration Tests ครอบคลุม:
- Login ด้วยข้อมูลถูกต้อง
- Login ด้วย Password ไม่ถูกต้อง
- Login ด้วย User ที่ไม่มีอยู่
- JWT หมดอายุหรือ Signature ไม่ถูกต้อง
- Access Endpoint โดยไม่มี Token
- Access Endpoint ด้วย Role ที่ไม่มีสิทธิ์
- บันทึก Login Log สำเร็จและล้มเหลว
- Logout และ Session Revocation หากรองรับ
- การเชื่อมต่อและ Mapping กับ PostgreSQL
รัน Test ด้วยคำสั่ง:
./mvnw test- ใช้ DTO แทนการส่ง Entity ออก API โดยตรง
- ให้ Business Logic อยู่ใน Service Layer
- ให้ Repository รับผิดชอบการเข้าถึงข้อมูล
- ใช้ Constructor Injection สำหรับ Dependency Injection
- ใช้ Transaction ให้เหมาะสมกับขอบเขตของ Business Operation
- หลีกเลี่ยงการสร้าง Utility Class ที่รวมหน้าที่หลายอย่างไว้ด้วยกัน
- ใช้ Migration Tool เช่น Flyway หรือ Liquibase หากต้องการควบคุมการเปลี่ยนแปลง Schema อย่างเป็นระบบ
- เขียน Tests สำหรับ Authentication และ Authorization ทุกกรณีสำคัญ
NNUXX@linux-deep-labs