แก้ปัญหา CORS ใน Spring Boot แบบถาวร (เลิกแปะ @CrossOrigin ทีละตัว)
"Access to XMLHttpRequest at '...' from origin 'http://localhost:3000' has been blocked by CORS policy"
ถ้าคุณทำ Frontend (React, Vue, Next.js) ต่อกับ Backend แล้วเจอตัวหนังสือสีแดงแบบนี้ใน Console ยินดีด้วยครับ คุณได้เจอกับ CORS (Cross-Origin Resource Sharing) ระบบรักษาความปลอดภัยของ Browser เข้าให้แล้ว
หลายคนเลือกทางแก้แบบลูกทุ่งคือเอา Annotation @CrossOrigin ไปแปะหัว Controller ทุกตัว... ซึ่งมัน "เหนื่อย" และ "ดูแลยาก" (เกิดวันนึงต้องเปลี่ยนกฎ ก็ต้องตามแก้ 50 ไฟล์)
วันนี้เรามาดูวิธีแก้แบบ Centralized (จุดเดียวจบ) กันครับ
วิธีที่ 1: โปรเจกต์ทั่วไป (ไม่มี Spring Security)
ถ้าโปรเจกต์ของคุณเป็น Web API ธรรมดา ไม่ได้ลง spring-boot-starter-security ไว้ วิธีที่ง่ายที่สุดคือสร้าง Class Configuration ขึ้นมาใหม่ เพื่อบอก Spring MVC ว่า "อนุญาตให้ใครเข้ามาได้บ้าง"
สร้างไฟล์ CorsConfig.java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 1. อนุญาตทุก Path ใน API
.allowedOrigins("http://localhost:3000", "https://mydomain.com") // 2. ระบุ Domain ของ Frontend (ห้ามใช้ * ใน Production)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 3. Method ที่อนุญาต
.allowedHeaders("*") // 4. Header ที่อนุญาต
.allowCredentials(true); // 5. อนุญาตให้ส่ง Cookie/Auth Header ได้
}
}
ข้อดี: ง่าย จบในไฟล์เดียว
ข้อควรระวัง: allowedOrigins("*") (อนุญาตทุกคน) ไม่ควรทำถ้ามีเรื่อง Authentication เพราะเสี่ยงโดนขโมย Session
วิธีที่ 2: โปรเจกต์ที่มี Spring Security (The Real Boss)
นี่คือจุดตายที่หลายคนตกม้าตาย!
บางคนทำวิธีที่ 1 แล้ว แต่ก็ยังติด CORS อยู่? สาเหตุเพราะ Spring Security นั้นทำงานก่อน Spring MVC ครับ
Filter ของ Security จะดัก Request ไว้ก่อน ถ้าเราไม่เปิดประตู CORS ใน Security Config ด้วย การตั้งค่าในวิธีที่ 1 ก็ไร้ความหมาย
วิธีแก้: Config ใน SecurityFilterChain
ในไฟล์ที่ประกาศ SecurityFilterChain ให้เพิ่ม .cors() เข้าไปดังนี้:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // API ปกติมักปิด CSRF
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // <--- เรียกใช้ Config ที่เราสร้าง
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
);
return http.build();
}
// สร้าง Bean นี้เพื่อให้ Spring Security รู้จักกฎ CORS
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000")); // อนุญาต Frontend
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration); // ใช้กฎนี้กับทุก Path
return source;
}
}
สรุป: เลือกใช้วิธีไหนดี?
- ถ้าไม่มี Spring Security: ใช้ วิธีที่ 1 (
WebMvcConfigurer) ง่ายและคลีนที่สุด - ถ้ามี Spring Security: ต้องใช้ วิธีที่ 2 (
CorsConfigurationSource) เท่านั้น เพราะถ้าใช้วิธีแรก Request จะโดน Security Block ตั้งแต่หน้าประตูครับ
เลิกแปะ @CrossOrigin ให้รก Code แล้วหันมาคุมกฎที่เดียว เพื่อชีวิตที่ง่ายขึ้นกันเถอะครับ!
No responses yet. Be the first to respond.