这个异常也是比较特殊的,所以我觉得有必要贡献出来。
背景
首先,无论客户端或者服务端,都已经做好可跨域请求的相关代码准备。所以,并不是简单的配置问题。
请求头:
OPTIONS /layout/saveLayout HTTP/1.1
Host: baby.sankuai.com
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Access-Control-Request-Headers: x-requested-with
Accept: */*
Accept-Encoding: gzip
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
响应:
HTTP/1.1 403 Forbidden
Server: Tengine
Date: Thu, 27 Sep 2018 07:33:59 GMT
Content-Length: 20
Connection: keep-alive
Keep-Alive: timeout=5
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type,x-requested-with,Authorization,access-token
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Invalid CORS request
可以看出,响应的 Access-Control-Allow-Origin、Access-Control-Allow-Methods,Access-Control-Allow-Headers,Allow 都包含了请求所需的内容,所以这个头配置这边妥妥的,没有问题。
(当然,如果这个也可以用来排查配置相关的问题。是否配置正确,把两个头拿出来看看就知道了,别整其它没用的。)
通过请求头可以看出,这是个options(预检)请求。在看响应头,返回状态码403,应该是预检失败了。
查看相应的服务器日志,找到如下报错信息:
java.lang.NoClassDefFoundError: javax/servlet/DispatcherType
一般的,这种异常信息,我第一反应就是jar包冲突。由于是javax/servlet下的,猜想和servlet-api有关。因为 servlet-api 在maven中配置的为 provided ,所以运行时使用的是 tomcat/lib 下的 servlet-api.jar。
经排查,发现3.0以下的jar包中是没有这个类的,只有3.0及以上才有。所以到tomcat的官网看了下版本信息对照表:
image.png
可以看到servlet 3.0 版本对应着 tomcat 7.0.x。
在本地使用tomcat 7 和 8 调式服务,没有发现问题。
登录跳板机查看现在机器tomcat版本,发现线上版本为 6.0.x版本。
于是又下载了 tomcat 6 的版本,重现了问题。经调试,发现是 Spring mvc 的 RequestMethodsRequestCondition (Spring mvc版本 4.3.7),使用了 DispatcherType ,而 tomcat 6 使用的 servlet 版本是 2.5,源码里并没有DispatcherType这个类。
解决
1.替换tomcat servlet版本
2.降级spring mvc版本
3.升级tomcat版本
替换与降级风险都不可控,故最后选择升级tomcat版本。
网友评论