OpenAPI Nâng Cao: Security, Grouping và Triết Lý API-First
Làm chủ kỹ thuật cấu hình JWT Security Scheme, phân nhóm GroupedOpenApi, ẩn endpoint nhạy cảm và so sánh chiến lược thiết kế API-first với code-first.
TL;DR: Để đưa tài liệu API vào môi trường thực tế an toàn, bài học này giải quyết 4 bài toán kỹ thuật nâng cao: (1) Khai báo @SecurityScheme cho JWT Bearer giúp Swagger UI tích hợp nút "Authorize", tự động gắn token vào header cho mọi lượt thử nghiệm; (2) Sử dụng GroupedOpenApi để chia nhỏ tài liệu thành các nhóm độc lập (Public, Admin, Internal); (3) Loại bỏ các endpoint bảo trì và debug qua @Hidden hoặc cấu hình exclude; (4) Xây dựng chiến lược phân tách tài liệu production để tránh rò rỉ diện mạo tấn công (attack surface). Cuối cùng, phân tích sự đánh đổi giữa hai triết lý phát triển API-First và Code-First theo quy mô đội ngũ.
1. Cấu hình Security Scheme JWT thế nào để Swagger UI có nút Authorize?
Khi các API yêu cầu xác thực JWT, lập trình viên cần cơ chế gửi kèm token khi bấm "Try it out" trên giao diện Swagger UI:
@Configuration
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
scheme = "bearer",
bearerFormat = "JWT",
in = SecuritySchemeIn.HEADER,
description = "Dien JWT token nhan duoc tu endpoint /api/auth/login"
)
public class OpenApiSecurityConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("TaskFlow API").version("v1.0.0"))
// Ap dung yeu cau token cho tat ca endpoint mac dinh
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"));
}
}
Đối với các endpoint không yêu cầu xác thực (như đăng nhập, đăng ký), sử dụng @SecurityRequirements rỗng để ghi đè:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@SecurityRequirements // Override global: khong hien thi o khoa va khong yeu cau token
@PostMapping("/login")
public TokenResponse login(@Valid @RequestBody LoginRequest req) {
return authService.login(req);
}
}
Trên Swagger UI, lập trình viên bấm vào nút Authorize, dán JWT token một lần duy nhất. Kể từ đó, toàn bộ request gửi qua "Try it out" sẽ tự động được đính kèm header Authorization: Bearer <token>.
2. GroupedOpenApi giúp chia nhỏ tài liệu API theo nhóm người dùng ra sao?
Khi hệ thống có hàng trăm endpoint bao gồm API người dùng, API quản trị và API giao tiếp nội bộ, việc gộp chung vào một tài liệu duy nhất gây rối loạn và tiềm ẩn rủi ro lộ cấu trúc quản trị.
GroupedOpenApi cho phép chia nhỏ tài liệu thành các đặc tả riêng biệt:
@Configuration
public class OpenApiGroupConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public-v1")
.displayName("1. Public API v1")
.pathsToMatch("/api/v1/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.displayName("2. Admin API")
.pathsToMatch("/api/admin/**")
.addOpenApiCustomizer(openApi ->
openApi.info(new Info().title("TaskFlow Admin Management").version("v1")))
.build();
}
}
Mỗi nhóm sẽ có đường dẫn tải tài liệu JSON riêng biệt:
- Nhóm Public:
/v3/api-docs/public-v1 - Nhóm Admin:
/v3/api-docs/admin
Trên giao diện Swagger UI, người dùng có thể chuyển đổi linh hoạt thông qua menu thả xuống "Select Definition" ở góc trên bên phải.
3. Làm thế nào để ẩn các endpoint nội bộ nhạy cảm khỏi tài liệu?
Sử dụng annotation @Hidden
Đặt @Hidden trên method hoặc controller class để chỉ thị cho springdoc bỏ qua khi sinh tài liệu:
@Hidden // Khong xuat hien trong OpenAPI spec
@GetMapping("/internal/metrics/memory")
public Map<String, Object> getMemoryMetrics() {
return systemService.getMemoryStats();
}
Ẩn hàng loạt qua cấu hình application.yml
springdoc:
paths-to-exclude: /internal/**, /debug/**, /actuator/**
packages-to-exclude: com.olhub.taskflow.internal
show-actuator: false
Annotation @Hidden chỉ loại bỏ thông tin khỏi file tài liệu, hoàn toàn không chặn việc truy cập mạng. Bất kỳ ai biết URL vẫn có thể gửi request thành công. Để bảo vệ thực sự, bạn bắt buộc phải cấu hình phân quyền qua Spring Security.
4. Nên tắt hay bảo vệ Swagger UI trên môi trường Production?
Để Swagger UI mở công khai trên môi trường Production mang lại nhiều rủi ro bảo mật:
- Lộ diện mạo tấn công (Attack Surface): Kẻ tấn công nắm rõ toàn bộ danh sách endpoint, các tham số bắt buộc và cấu trúc dữ liệu nội bộ.
- Lộ quy tắc kiểm thực: Schema tài liệu để lộ regex, độ dài tối thiểu của mật khẩu, giúp tin tặc xây dựng kịch bản brute-force chính xác hơn.
- Công cụ dò quét miễn phí: Tính năng "Try it out" vô tình biến thành giao diện tiện lợi cho kẻ xấu thực hiện tấn công dò lỗi.
Các chiến lược triển khai Production:
- Vô hiệu hóa hoàn toàn trên Production (Khuyến nghị):
YAML
# application-prod.yml springdoc: api-docs: enabled: false swagger-ui: enabled: false - Bảo vệ bằng Spring Security (Dành cho nội bộ công ty):
Yêu cầu tài khoản có quyền
ROLE_DEVELOPERmới được phép truy cập vào/swagger-ui.htmlvà/v3/api-docs/**.
5. So sánh mô hình API-First và Code-First theo quy mô dự án ra sao?

Bảng đối chiếu hai triết lý phát triển:
| Tiêu chí | Code-First (springdoc mặc định) | API-First (Design-first) |
|---|---|---|
| Thời điểm có Spec | Sau khi viết xong mã nguồn Controller | Trước khi bắt đầu viết bất kỳ dòng code nào |
| Tiến độ Frontend | Phải chờ Backend hoàn thành mới tích hợp | Phát triển song song ngay từ ngày đầu qua Mock Server (Prism) |
| Nguồn sự thật | Mã nguồn Java (Single Source of Truth) | File openapi.yaml (Hợp đồng thiết kế) |
| Độ phức tạp Setup | Rất thấp (chỉ cần thêm 1 dependency) | Cần cấu hình plugin openapi-generator-maven-plugin |
| Ngữ cảnh phù hợp | Nhóm nhỏ (1-5 người), chu kỳ lặp nhanh | Đội ngũ lớn (> 10 người), dự án đa nền tảng, đối tác ngoài |
6. Những cạm bẫy bảo mật thường gặp với tài liệu OpenAPI là gì?
- Quên miễn trừ các endpoint Public: Khai báo Security Scheme toàn cục mà quên gắn
@SecurityRequirementstrên/api/auth/loginkhiến người dùng không thể thử nghiệm đăng nhập trên UI. - Khai báo GroupedOpenApi rỗng: Khởi tạo
GroupedOpenApinhưng quên gọipathsToMatch(), dẫn đến việc cả nhóm con vẫn nạp toàn bộ 100% endpoint của hệ thống. - Phụ thuộc vào bảo mật bằng cách che giấu (Security through Obscurity): Đổi đường dẫn
/swagger-ui.htmlthành chuỗi ngẫu nhiên mà không bật xác thực; các công cụ quét tự động vẫn có thể dò ra dễ dàng.
7. OpenAPI nâng cao kết nối với các thành phần khác ra sao?
- OpenAPI & springdoc Cơ Bản: Cung cấp các thao tác scan và sinh tài liệu nền tảng.
- Mini-challenge TaskFlow v1: Thử thách tổng hợp áp dụng toàn diện JWT Security Scheme, Grouping và Validation trong ứng dụng thực tế.
8. Những điểm then chốt cần ghi nhớ là gì?
- Khai báo
@SecuritySchemevà gắn@SecurityRequirementđể kích hoạt nút Authorize hỗ trợ JWT trong Swagger UI. - Sử dụng
GroupedOpenApiđể phân tách tài liệu API rõ ràng theo vai trò người dùng (Public vs Admin). @Hiddenchỉ ẩn khỏi tài liệu, không có tác dụng bảo vệ an ninh tầng mạng.- Vô hiệu hóa hoặc đặt mật khẩu bảo vệ Swagger UI trên môi trường Production.
- Cân nhắc lựa chọn giữa Code-First và API-First tùy thuộc vào quy mô nhân sự và mức độ song song hóa công việc.
9. Bài tập tự kiểm tra mức độ hiểu sâu
- Q1Annotation nào được sử dụng để loại bỏ yêu cầu xác thực JWT cho một endpoint công khai (như /api/auth/login) khi hệ thống đã bật SecurityRequirement toàn cục?
- Q2Tại sao việc chỉ gắn @Hidden lên một controller method nhạy cảm (như xóa dữ liệu) là chưa đủ an toàn?
- Q3Bean nào trong springdoc-openapi được sử dụng để phân chia tài liệu API thành nhiều nhóm riêng biệt với đường dẫn URL độc lập?
- Q4Lợi ích cốt lõi nhất của việc áp dụng mô hình API-First đối với đội ngũ phát triển bao gồm cả Frontend và Backend là gì?
- Q5Biện pháp xử lý nào được khuyến nghị cho Swagger UI khi triển khai ứng dụng lên môi trường Production công khai?
Bài tiếp theo: Mini-challenge — TaskFlow REST API v1
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
OpenAPI và springdoc: Tự Động Sinh Tài Liệu REST API
Bài tiếpMini-challenge: TaskFlow REST API v1 — capstone phần REST API
Lộ trình Junior Java Backend Developer
Chặng 15/21 · REST API với Spring MVC — bài 19/21 của chặng
Thảo luận & góp ý
Hỏi đáp về bài này