Spring Boot bài 19: CSRF, CORS và gọi API có session đúng cách
- 18-10-2026
- Toanngo92
- 0 Comments
Giáo trình Spring Boot MVC và REST API · Chương 8/12: Đăng nhập, phân quyền và bảo vệ request · Bài 19/28 · Biên soạn tháng 10/2026.
Mục lục
Mục tiêu bài học
Phân biệt CSRF với CORS, giữ bảo vệ cho request ghi và gửi token cùng cookie khi kiểm tra API.
CSRF bảo vệ request dùng thông tin xác thực tự gửi
Trình duyệt có thể tự gửi cookie đăng nhập khi gọi một website. Nếu website nhận POST thay đổi dữ liệu mà không xác minh nguồn yêu cầu phù hợp, một trang khác có thể dụ trình duyệt gửi thao tác thay người dùng. CSRF token là một phần cơ chế chống tình huống đó.
Mẫu giữ CSRF mặc định cho cả form và API vì ứng dụng có session và HTTP Basic. Tên “REST API” hoặc body JSON không tự chứng minh mọi rủi ro CSRF đã biến mất. Nếu xây API stateless dùng bearer token, cần thiết kế riêng dựa trên cách token được lưu và gửi; không disable CSRF toàn bộ ứng dụng chỉ để Postman chạy được.
Token và session đi cùng nhau
Form Thymeleaf nhận token qua input ẩn. Client API lấy token từ GET /api/csrf sau khi có xác thực teacher, giữ cookie session rồi gửi token trong header mà endpoint trả về. Endpoint trả Cache-Control: no-store để tránh cache token. Không copy token của một session sang session khác rồi mong nó còn hợp lệ.
Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/CourseApiController.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.
public record Token(String headerName,String parameterName,String token){}
@GetMapping("/csrf") ResponseEntity<Token> csrf(CsrfToken token){return ResponseEntity.ok().cacheControl(CacheControl.noStore()).body(new Token(token.getHeaderName(),token.getParameterName(),token.getToken()));}
# PowerShell: ứng dụng đang chạy và biến mật khẩu demo đã được đặt
$pair = "teacher:$env:HVC_DEMO_PASSWORD"
$basic = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($pair))
$headers = @{ Authorization = "Basic $basic" }
$response = Invoke-WebRequest http://localhost:8080/api/csrf -Headers $headers -SessionVariable session
$csrf = $response.Content | ConvertFrom-Json
$headers[$csrf.headerName] = $csrf.token
$body = @{title="API demo"; price=0; categoryId=1} | ConvertTo-Json
Invoke-RestMethod http://localhost:8080/api/courses -Method Post -Headers $headers -WebSession $session -ContentType "application/json" -Body $body
CORS là chính sách trình duyệt, không phải đăng nhập
Origin gồm scheme, host và port. http://localhost:3000 khác http://localhost:8080. Nếu frontend ở origin khác, trình duyệt áp dụng CORS và có thể gửi OPTIONS preflight trước request thật. Cấu hình phải nêu origin, method và header được phép; khi dùng credentials, không thể đơn giản dùng wildcard origin cho mọi nơi.
SecurityConfig nối rõ CorsConfigurationSource vào SecurityFilterChain. Mẫu chỉ cho origin lấy từ hvc.allowed-origin, mặc định localhost:3000, và header X-CSRF-TOKEN. Khi chuyển môi trường phải thay origin đúng; không giữ danh sách localhost như cấu hình production.
Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/SecurityConfig.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.
@Bean CorsConfigurationSource cors(AppSettings settings){
CorsConfiguration c=new CorsConfiguration();c.setAllowedOrigins(List.of(settings.allowedOrigin()));
c.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS"));c.setAllowedHeaders(List.of("Content-Type","Authorization","X-CSRF-TOKEN"));c.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/api/**",c);return source;
}
Chẩn đoán có thứ tự
Nếu OPTIONS bị từ chối hoặc thiếu Access-Control-Allow-Origin, xem cấu hình CORS. Nếu POST đã tới server nhưng bị 403, xem quyền và CSRF. Nếu dùng curl thành công nhưng trình duyệt thất bại, sự khác biệt có thể nằm ở CORS hoặc cookie; curl không áp chính sách CORS như trình duyệt.
Kiểm thử mẫu có origin được cho phép và origin lạ, đồng thời kiểm tra request ghi thiếu CSRF bị chặn. Token cần lấy lại sau thay đổi phiên như đăng nhập/đăng xuất theo luồng client. Không đưa token hay Authorization vào ảnh chụp, log công khai hoặc mã nguồn.
Lỗi thường gặp và lưu ý
- CORS không ngăn một chương trình ngoài trình duyệt gửi request tới server.
- Không sửa 403 bằng cách bỏ cả CSRF, CORS và phân quyền cùng lúc.
Ghi nhớ
CSRF xác minh request trong mô hình phiên; CORS điều khiển đọc response xuyên origin trên trình duyệt; cả hai không thay thế quyền truy cập.

