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错误。
修复步骤
在SecurityConfig中放行所有OPTIONS请求
修改configure(HttpSecurity httpSecurity)方法,在authorizeRequests()下添加一行:httpSecurity .csrf().disable() .authorizeRequests() // 新增:允许所有OPTIONS请求通过 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 原有配置保留 .antMatchers("/api/auth/**") .permitAll() ... // 其他原有配置检查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验证逻辑 ... }为什么之前没有问题?
之前未添加拦截器时,请求没有自定义头,浏览器不会发送CORS预请求,所以Spring Security的配置缺陷没有暴露。添加拦截器后触发预请求,才显现出OPTIONS请求被拦截的问题。
内容的提问来源于stack exchange,提问作者magicalassembler
相关产品推荐
相关产品推荐

