Angular调用Spring Boot API遇CORS错误,预检请求未通过
问题描述
后端基于Spring Boot开发,运行在localhost:9090;前端基于Angular开发,运行在localhost:4200。调用后端接口时触发CORS错误:
从源地址'http://localhost:4200'访问'http://localhost:9090/vendor-data/'的XMLHttpRequest被CORS policy阻止:预检请求的响应未通过访问控制检查:不具备HTTP ok状态。
相关代码
前端代码
Service文件
getVendorData(headersData:any):Observable<any>{ return this.http.get('http://localhost:9090/vendor-data/',{headers:headersData}); }
Component.ts文件
ngOnInit() { const token = localStorage.getItem('token'); const headers = new HttpHeaders({ Authorization: `Bearer ${token}`, }); console.log(this.data.getVendorData(headers)) this.data.getVendorData(headers).subscribe({ next: (v) => { this.vendorList = v; }, error: (e) => {}, complete: () => { }, }); }
后端代码
CORS配置类
@Configuration @EnableWebMvc public class VendorConfiguration implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("http://localhost:4200") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true); } }
JWT过滤器类
public class DataFilter extends OncePerRequestFilter{ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token=extractToken(request); boolean isError=true; if(token!=null && token.length()>6) { try { Jwts .parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(Decoders.BASE64URL.decode( "f9b2b91e3c468f1937598573b00d923426127dc06bf6cc8b446fac9ec1364f88"))) .build() .parseClaimsJws(token) .getBody(); isError=false; filterChain.doFilter(request, response); } catch(SignatureException e) { System.out.print("Invalid Signature"); } catch(MalformedJwtException e) { System.out.print("Invalid Token"); } } if(isError) { (response).sendError(HttpStatus.UNAUTHORIZED.value(),"Unauthorized"); } } private String extractToken(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); String token = null; if (authHeader != null) { token = authHeader.substring(7); } return token; } }
已尝试在Controller类添加@CrossOrigin("*")注解,问题依旧。疑问:是否是过滤器类导致了该错误?
问题原因及解决方案
是的,这个JWT过滤器就是导致CORS预检失败的核心原因。
浏览器发送的CORS预检请求(OPTIONS方法)不会携带Authorization头,但你的过滤器会直接执行token校验逻辑,发现没有有效token后返回401 Unauthorized状态码。而CORS预检请求要求响应必须返回200 OK或其他允许的状态码,因此触发了CORS错误。
具体解决方案
- 让过滤器跳过OPTIONS预检请求
修改过滤器的doFilterInternal方法,先判断请求方法是否为OPTIONS,若是则直接放行,不执行token校验:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 跳过OPTIONS预检请求,直接放行 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { filterChain.doFilter(request, response); return; } String token=extractToken(request); boolean isError=true; if(token!=null && token.length()>6) { try { Jwts .parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(Decoders.BASE64URL.decode( "f9b2b91e3c468f1937598573b00d923426127dc06bf6cc8b446fac9ec1364f88"))) .build() .parseClaimsJws(token) .getBody(); isError=false; filterChain.doFilter(request, response); } catch(SignatureException e) { System.out.print("Invalid Signature"); } catch(MalformedJwtException e) { System.out.print("Invalid Token"); } } if(isError) { response.sendError(HttpStatus.UNAUTHORIZED.value(),"Unauthorized"); } }
- 完善CORS配置
在CORS配置中显式允许Authorization头和OPTIONS方法,确保预检请求能正确识别允许的自定义头:
public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:4200") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("Authorization", "Content-Type") .allowCredentials(true); }
额外注意事项
- 确保过滤器的注册顺序正确,CORS相关处理需优先于JWT过滤器执行(Spring Boot默认CORS配置会在过滤器前处理,但跳过OPTIONS请求是最直接的解决方式)。
- 后续需检查前端token的有效性,避免因token无效导致的401错误,但当前CORS问题的根源已解决。
内容的提问来源于stack exchange,提问作者Arnab Das
相关产品推荐
相关产品推荐

