You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot+Angular添加博客时出现CORS预检请求错误求助

问题:SpringBoot+Angular提交博客时的CORS预请求错误

我使用SpringBoot和Angular开发代理网站,新增了基于TinyMCE编辑器的博客添加功能。每次尝试提交博客时,都会出现如下CORS错误:

Access to XMLHttpRequest at 'http://localhost:8080/api/blogs/createBlog' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

尽管后端已配置Webconfig处理跨域,但添加前端HTTP拦截器后开始出现该问题。以下是相关的前后端代码:

前端相关代码

HTTP拦截器代码

@Injectable()
export class HttpClientInterceptor implements HttpInterceptor {
  constructor(private $localStorage: LocalStorageService) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.$localStorage.retrieve("authenticationToken");
    console.log('jwt token ' + token);

    if (token) {
      const cloned = req.clone(
        {
          headers: req.headers.set("Authorization", "Bearer " + token)
        }
      );

      return next.handle(cloned);
    } else {
      return next.handle(req);
    }
  }
}

BlogService代码

export class BlogService {

  constructor(private http: HttpClient) {
  }

  addBlog(blogPayload: BlogPayload) {
    return this.http.post('http://localhost:8080/api/blogs/createBlog', blogPayload)
  }
}

BlogFormComponent代码

export class BlogFormComponent {
  blogForm: FormGroup;
  title = new FormControl('')
  body = new FormControl('')
  blogPayload: BlogPayload;


  constructor(
    private blogService: BlogService,
    // private toastr: ToastrService
  ) {
    this.blogForm = new FormGroup({
      title: this.title,
      body: this.body
    })

    this.blogPayload = {
      id: '',
      content: '',
      title: '',
      email: '',
    }
  }

  addPost() {
    this.blogPayload.content = this.blogForm.get('body')?.value;
    this.blogPayload.title = this.blogForm.get('title')?.value;
    this.blogService.addBlog(this.blogPayload).subscribe(data => {
      // this.toastr.success("Blog with title: '" +this.title + "' has been successfully added")
      console.log("success")
    }, err => {
      // this.toastr.error("There was a problem with adding the blog. Try again later or contact the admin.")
      console.log("error")
    })
  }
}

后端相关代码

Webconfig配置

@Configuration
public class Webconfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("*")
                .maxAge(3600L)
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}

SecurityConfig配置

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        return new JwtAuthenticationFilter();
    }

    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/auth/**")
                .permitAll()
                .antMatchers(HttpMethod.GET, "/api/client/**", "/api/project/**", "/api/provider/**", "/api/team/**", "/api/category/**", "/api/blogs/**")
                .permitAll()
                .antMatchers(HttpMethod.GET, "/api/contact/**")
                .authenticated()
                .antMatchers(HttpMethod.POST, "/api/contact/**")
                .permitAll()
                .antMatchers(HttpMethod.POST, "/api/client/**", "/api/project/**", "/api/provider/**", "/api/team/**", "/api/category/**", "/api/auth/**", "/api/blogs/**")
                .authenticated()
                .antMatchers(HttpMethod.PUT, "/api/contact/**", "/api/client/**", "/api/project/**", "/api/provider/**", "/api/team/**", "/api/category/**", "/api/blogs/**")
                .authenticated()
                .antMatchers(HttpMethod.DELETE, "/api/contact/**", "/api/client/**", "/api/project/**", "/api/provider/**", "/api/team/**", "/api/category/**", "/api/blogs/**")
                .authenticated()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

问题排查与解决方法

核心原因

添加前端拦截器后,请求带上了Authorization自定义头,触发浏览器发送CORS预请求(OPTIONS方法)。但你的Spring Security配置中没有放行OPTIONS请求,导致预请求被拦截返回非200状态码,触发CORS错误。

修复步骤

  1. 在SecurityConfig中放行所有OPTIONS请求
    修改configure(HttpSecurity httpSecurity)方法,在authorizeRequests()下添加一行:

    httpSecurity
            .csrf().disable()
            .authorizeRequests()
            // 新增:允许所有OPTIONS请求通过
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            // 原有配置保留
            .antMatchers("/api/auth/**")
            .permitAll()
            ... // 其他原有配置
    
  2. 检查JWT拦截器是否处理OPTIONS请求
    打开你的JwtAuthenticationFilter类,在doFilterInternal方法开头添加判断,直接放行OPTIONS请求:

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 新增:直接放行OPTIONS请求
        if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        // 原有JWT验证逻辑
        ...
    }
    
  3. 为什么之前没有问题?
    之前未添加拦截器时,请求没有自定义头,浏览器不会发送CORS预请求,所以Spring Security的配置缺陷没有暴露。添加拦截器后触发预请求,才显现出OPTIONS请求被拦截的问题。

内容的提问来源于stack exchange,提问作者magicalassembler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 23:55:55