CORS跨域问题服务端的一个解决方法

上次写了一篇吐槽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请求的响应:

CORS跨域问题服务端的一个解决方法_第1张图片

返回的支持的方法里只有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跨域问题服务端的一个解决方法_第2张图片

 

 

你可能感兴趣的:(日常排错)