先说结论
增加一个过滤器类即可:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {
private static final String OPTIONS = "OPTIONS";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
res.addHeader("Access-Control-Allow-Headers", "*");
res.addHeader("Access-Control-Max-Age", "3600");
// 如果是OPTIONS则结束请求
if (OPTIONS.equals(((HttpServletRequest) request).getMethod())) {
response.getWriter().println("ok");
return;
}
chain.doFilter(request, response);
}
}
问题分析
技术说明
使用的是SpringBoot构建了一个java后端服务。
问题出现时情况
- 已经配置了CORS的configuration,代码如下:
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
.allowedHeaders("*")
.maxAge(3600);
}
}
- 对于简单请求,可以直接发送并获取信息,这要归功于CorsConfiguration,没有它简单请求也是跨域的。
- 但是当我自行设定了一个header之后,再发出的请求将为跨域请求,浏览器报错信息为:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
,说的很明确了,响应缺少一个运行该源的头。 - 对于简单请求,响应头部有Access-Control-Allow-Origin的属性。
问题理解
- 网上很多都在说明什么是跨域请求,怎么个流程,这里就不再赘述了。
- 需要说明的是,复杂请求会先发送的这个预检请求的方法是OPTIONS,但是及时在CorsConfiguration中allowedMethods中添加该方法依然无济于事。那么此时我们应该对这个预检请求进行单独处理。
- 此时即有了问题结论中的过滤器类,并且我们将其声明为最高等级,遇到了OPTIONS方法直接返回并携带对应的头部。
未解决的问题
这里其实有一个很奇怪的现象,在我的这个后端实现中,对于/user路径下的接口,并不需要额外的配置即可获取复杂请求并响应,一旦不在/user路径下,相同的前端请求,相同的后端配置,就会发生跨域,这一点暂时还没有得到清晰的解决。
在添加了filter之后,所有的请求均可以正常响应了。