本地开发时Next.js与SpringBoot的CORS配置异常排查
我正尝试从Next.js前端(localhost:3000)向Java SpringBoot服务端(localhost:5555)发起本地网络请求,但SpringBoot服务端持续出现以下错误:
[nio-3000-exec-2] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for OPTIONS "/error", parameters={}
[nio-3000-exec-1] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 403
我的WebMvcConfigurer配置如下:
package com.codaholic.shop.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfiguration implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:5555") .exposedHeaders("*") .allowedMethods("*") .allowedHeaders("*"); } }
Next.js前端控制台的错误日志如下:
Access to fetch at 'http://localhost:3000/api/v1/article' from origin 'http://localhost:5555' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
/api/v1/article接口的代码如下:
@GetMapping("") public ResponseEntity<List<Article>> getArticleByUser() throws ResourceNotFoundException { CustomUserDetails userDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userDetails.getUserId(); List<Article> articles = articleService.getArticlesByUserId(userId); return ResponseEntity.ok().body(articles); };
不过发起登录接口的API请求时不会遇到相同错误,我不确定缺少哪些配置或该如何调试。当前使用的是SpringBoot 3.1.3版本。
补充说明1
另外补充一点,我已通过以下配置禁用了csrf:SecurityConfiguration.java
@Configuration @EnableWebSecurity @RequiredArgsConstructor @EnableMethodSecurity public class SecurityConfiguration { private final JwtAuthenticationFilter jwtAuthFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf((csrf) -> csrf.disable() ) .authorizeHttpRequests((authorize)-> authorize .dispatcherTypeMatchers(DispatcherType.ERROR).permitAll() .requestMatchers( "/api/v1/auth/**", "/swagger-ui/**", "/swagger", "/swagger-ui.html" ) .permitAll() .anyRequest() .authenticated() ) .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
1. 修正CORS配置的源地址
你的WebMvcConfiguration里allowedOrigins写反了——前端运行在localhost:3000,服务端是localhost:5555,允许的跨域源应该是前端地址,同时添加allowCredentials支持认证头携带:
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:3000") // 改为前端地址 .exposedHeaders("*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true); // 支持携带JWT等认证信息 }
2. 让Spring Security放行OPTIONS预请求
预请求(OPTIONS)属于跨域必须的请求类型,需要在Security配置里明确放行:
修改securityFilterChain中的authorizeHttpRequests部分:
.authorizeHttpRequests((authorize)-> authorize .dispatcherTypeMatchers(DispatcherType.ERROR, DispatcherType.OPTIONS).permitAll() // 添加OPTIONS放行 .requestMatchers( "/api/v1/auth/**", "/swagger-ui/**", "/swagger", "/swagger-ui.html" ) .permitAll() .anyRequest() .authenticated() )
3. 避免JWT过滤器拦截OPTIONS请求
登录接口正常是因为它无需认证,但/api/v1/article需要JWT,检查你的JwtAuthenticationFilter,确保它跳过OPTIONS请求:
在doFilterInternal方法开头添加判断:
if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; }
4. 验证CORS配置是否生效
用Postman发送OPTIONS请求到http://localhost:5555/api/v1/article,检查响应头是否包含以下字段:
Access-Control-Allow-Origin: http://localhost:3000Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETEAccess-Control-Allow-Headers: *
如果这些头存在,说明CORS配置生效,问题可能出在认证环节;如果不存在,检查WebMvcConfiguration是否被Spring正确扫描(比如包路径是否在SpringBoot启动类的扫描范围内)。
内容的提问来源于stack exchange,提问作者jcleow

