Vue.js调用带JWT的Spring Boot接口遇CORS拦截问题求助
问题现象
使用Vue.js前端调用Spring Boot后端接口,携带JWT令牌请求时触发CORS错误,错误信息如下:
"Access to fetch at 'http://127.0.0.1:8081/api/v1/posts/userPosts' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
"PostService.js:12 GET http://127.0.0.1:8081/api/v1/posts/userPosts net::ERR_FAILED"
已尝试添加@CrossOrigin注解、全局CorsConfig配置类,但均无效;仅当Spring Security放开所有/api/v1/**接口无需认证时请求正常,限制认证后就触发CORS错误。
核心原因
带Authorization头的请求属于非简单请求,浏览器会先发OPTIONS预检请求,这个请求不会携带JWT令牌,被Spring Security的认证拦截,导致CORS配置的响应头无法返回,进而触发浏览器的CORS错误。
解决方案
1. 让Spring Security启用CORS配置
在SecurityConfig的securityFilterChain方法中添加.cors(withDefaults()),让Spring Security应用你已配置的全局CORS规则:
package com.blog.security.config; import com.blog.user.User; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; @Bean public SecurityFilterChain securityFilterChain(@NotNull HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf() .disable() .cors(withDefaults()) // 启用CORS配置 .authorizeHttpRequests() .requestMatchers("/api/v1/posts","/api/v1/auth/register","/api/v1/auth/login") .permitAll() .anyRequest() .authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return httpSecurity.build(); } }
2. 放行OPTIONS预检请求
在SecurityConfig中添加对OPTIONS请求的全局放行,确保预检请求不被认证拦截:
// 在authorizeHttpRequests中添加: .requestMatchers(HttpMethod.OPTIONS).permitAll()
完整修改后的securityFilterChain:
@Bean public SecurityFilterChain securityFilterChain(@NotNull HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf() .disable() .cors(withDefaults()) .authorizeHttpRequests() .requestMatchers(HttpMethod.OPTIONS).permitAll() // 放行OPTIONS请求 .requestMatchers("/api/v1/posts","/api/v1/auth/register","/api/v1/auth/login") .permitAll() .anyRequest() .authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return httpSecurity.build(); }
3. 确保JWT过滤器不拦截OPTIONS请求
如果自定义了JwtAuthenticationFilter,在doFilterInternal方法中添加判断,直接放行OPTIONS请求:
@Override protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException { // 放行OPTIONS预检请求 if (HttpMethod.OPTIONS.name().equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; } // 原有JWT校验逻辑 String authHeader = request.getHeader("Authorization"); String jwt = null; String username = null; // ... 后续逻辑 }
额外验证
- 确认
CorsConfig中的allowedOrigins是前端实际的地址http://localhost:8080,与浏览器控制台显示的origin一致 - 前端请求的
Content-Type为application/json,属于非简单请求,必然触发预检,需确保上述配置生效
内容的提问来源于stack exchange,提问作者Ondra David

