Vaadin Spring Boot中Post请求遭CORS拦截问题求助
我在Vaadin Java Spring Boot应用里搭建了REST控制器,所有@GetMapping接口均可正常运行,但@PostMapping接口从Angular端调用时始终报CORS错误:
Access to XMLHttpRequest at <my_page> from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource。
从零新建普通Spring Boot应用时Post请求功能正常,但当前Vaadin应用中调整SecurityConfig和WebConfig参数后仍无法解决。相关代码如下:
REST控制器代码
@RestController @RequestMapping("/api") @CrossOrigin(origins = "*") // Allow requests from any origin public class RESTController { @GetMapping("/hello") public String sayHello() { return "hello"; } @PostMapping("/print") public Map<String, Object> printMessage(@RequestBody Map<String, Object> payload) { System.out.println("Received JSON: " + payload); payload.put("status", "Received successfully"); return payload; } }
SecurityConfiguration代码
@EnableWebSecurity @Configuration public class SecurityConfiguration extends VaadinWebSecurity { @Autowired CrmService crmService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth.requestMatchers( new AntPathRequestMatcher("/jobs/images/**"), // Allow public access to images new AntPathRequestMatcher("/api/getfields/**"), // Allow public access to /api/getfields/** new AntPathRequestMatcher("/api/**"), new AntPathRequestMatcher("/images/*.jpg"), new AntPathRequestMatcher("/**/*.png"), new AntPathRequestMatcher("/**/*.jpg"), new AntPathRequestMatcher("/**/*.svg"), new AntPathRequestMatcher("/**/*.css"), new AntPathRequestMatcher("/**/*.ts"), new AntPathRequestMatcher("/**/*.js"), new AntPathRequestMatcher("/**/*.html") ) .permitAll()); setLoginView(http, LoginView.class); http.headers(headers -> headers .contentSecurityPolicy(csp -> csp .policyDirectives("frame-ancestors 'self'") ) ); super.configure(http); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(WebSecurity web) throws Exception { // Customize your WebSecurity configuration. super.configure(web); } @Bean public UserDetailsService userDetailsService() { return new CustomUserDetailsService(crmService); } }
WebConfig代码
@Configuration public class WebConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); registry.addMapping("/api/getfields/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); } }; } }
解决方案
问题核心在于Vaadin Security的规则优先级、OPTIONS预检请求被拦截,以及CORS配置的冲突,按以下步骤调整:
在SecurityConfig中放行OPTIONS请求并禁用API的CSRF
跨域Post请求会先发送OPTIONS预检请求,必须确保该请求不被拦截;同时REST接口不需要CSRF保护,可直接忽略:@Override protected void configure(HttpSecurity http) throws Exception { // 禁用API路径的CSRF http.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**")); // 先放行所有OPTIONS请求,再处理其他规则 http.authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers( new AntPathRequestMatcher("/jobs/images/**"), new AntPathRequestMatcher("/api/**"), new AntPathRequestMatcher("/images/*.jpg"), new AntPathRequestMatcher("/**/*.png"), new AntPathRequestMatcher("/**/*.jpg"), new AntPathRequestMatcher("/**/*.svg"), new AntPathRequestMatcher("/**/*.css"), new AntPathRequestMatcher("/**/*.ts"), new AntPathRequestMatcher("/**/*.js"), new AntPathRequestMatcher("/**/*.html") ).permitAll()); setLoginView(http, LoginView.class); http.headers(headers -> headers .contentSecurityPolicy(csp -> csp .policyDirectives("frame-ancestors 'self'") ) ); // 自定义规则要放在super.configure之前,避免被覆盖 super.configure(http); }简化CORS配置,避免重复冲突
移除REST控制器上的@CrossOrigin注解,用全局配置统一处理,同时建议指定具体Origin而非通配符(生产环境更安全):@Configuration public class WebConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:4200") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) // 若需要携带Cookie则开启 .maxAge(3600); } }; } }验证调试
重启应用后,打开浏览器开发者工具的Network面板,查看OPTIONS请求的响应头,确认Access-Control-Allow-Origin、Access-Control-Allow-Methods等关键头是否正确返回。
内容的提问来源于stack exchange,提问作者user2859896

