上次写了一篇吐槽CORS的博客之后,今天再次出现此类问题。虽然对CORS已经了解的比较透彻,但这次的问题是一系列连锁问题引起的,最终对外表现为与一个不相关的错误,比较有迷惑性,所以有必要记录下来。
1.错误表现
先贴前端的报错异常:
OPTIONS http://foo.com/test 405 (Method Not Allowed)
Failed to load http://foo.com/test: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://originfoo.com' is therefore not allowed access.
这是服务端不支持CORS跨域的典型错误,导致接下来的真实请求被拦截了,而从预检请求的响应上看,也是服务端没有提供CORS支持,下面是OPTIONS请求的响应:

返回的支持的方法里只有GET和POST。
2.错误排查
首先需要声明的是,我们的系统本来有一个全局拦截器为所有请求设置CORS跨域头部,但这个预检请求收到的响应,Allow-Methods里面仍然没有OPTIONS方法,所以脑子直一点的可能认为是拦截器的代码出问题了。但是他就是找到天荒地老也不会找到一个错误的,因为我们的请求会经过很多道拦截器,所以我们需要换个思路了。
通过后台日志打印traceId可以发现,这个请求走到LoginInterceptor就断掉了,登录拦截器检测到未登录就会返回403错误。(那么问题来了,既然我后台返回的403响应,为什么到了浏览器变成405了,别着急,答案就在最后一段)
这让问题变得更加扑朔迷离,前端显示用户明明是登录状态。
在大脑里排除了其他所有可能之后,我意识到,这里请求被登录拦截器拦截了,一定是因为前端没有传sessionId。
然后猛然想起,这是OPTIONS请求,的确是一个参数和cookie都不会带的喔。
结论:所有问题的根源,是因为预检请求不会携带任何请求参数和cookie。
3.解决方法
解决方法就是,为了让预检请求顺利得到他想要的跨域头部,就要在全局拦截器里(这个全局拦截器必须放在其他业务拦截器的最前面),对预检请求做一个特殊处理。代码如下:
public void intercept(Invocation arg0) {
HttpServletRequest request=arg0.getController().getRequest();
HttpServletResponse response=arg0.getController().getResponse();
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers",
"Content-Type, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, " +
"Last-Modified, Cache-Control, Expires");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
if(OPTIONS_METHOD.equals(request.getMethod())){
logger.info("options method");
arg0.getController().renderJson();
}else {
arg0.invoke();
}
}
这里我们检测到,如果请求方法是OPTIONS(预检专用请求方法),请求就不传递下去了,直接返回一个不带任何内容的正常响应(200)
4.问题分析
我们来从头到尾分析一下,OPTIONS预检请求不携带任何参数和cookie,是如何导致前端收到405响应的。
这里画了一张简图,应该很明了了

本文详细记录了在遇到CORS跨域问题时,由于预检请求(OPTIONS)不携带参数和cookie导致服务端返回403错误,进而前端显示405错误的排查过程。解决方案是在全局拦截器中对OPTIONS请求进行特殊处理,返回带有CORS支持的200响应。
2931

被折叠的 条评论
为什么被折叠?



