本文主要给大家介绍了关于Javascript跨域后台设置拦截的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍吧。
子域名之间互相访问需要跨域
结论放在开头:
1.服务端必须设置允许跨域
2.客户端带cookie需要设置 withCredentials
3.无论服务端是否允许跨域,该request都会完整执行
4. options 预请求需要设置返回空,不然requestMapping没有支持该方法则出错
环境搭建
需求
首先需要搭建两个环境。一个是提供API的server A,一个是需要跨域访问API的server B。
Server A提供了一个api。完整的请求request是:
https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759&_=1490855801818
Server B有个页面page:
http://cros.corstest.com.net:3001/test.html
并且这个page需要请求server A的api。
但由于跨域保护,请求失败:
修改host
首先本地配置两个指向127.0.0.1的host,方便互相跨域。
启动项目A,方便提供API。
至于项目B,测试跨域只要写个html静态页面即可。那么就写一个test.html,并通过一个工具发布:
安装
本地启动一个test.html
关于跨域CORS
ruanyifeng 的文章里说浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
其中同时满足一下2种标准即为简单跨域:
- HEAD
- GET
- POST
2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
而其他情况,非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUT 或 DELETE ,或者 Content-Type 字段的类型是 application/json 。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),即 options 请求。
关键
跨域的关键是浏览器获得服务器的认可,而服务器的认可就是header里的 Access-Control-Allow-Origin 。浏览器通过比较服务端返回的response中是否包含这个字段,以及包含这个字段的内容是否是当前网址来确定是否跨域。也就是说绕过浏览器是可以不用跨域的。
有个问题,看好多文章并没有指出。
第一点,带cookie问题。浏览器设置 withCredentials 为 true 则会带cookie发送给服务端。而服务端设置 Access-Control-Allow-Credentials 为 true 则接收, false 则不接受。关键是到filter里的时候才会决定是否设置response,那么这时候cookie已经存在request里了吧。(待验证)
验证:
server端确实已经接受了cookie,即使设置为false,服务端仍旧接受cookie。而客户端也仍旧可以发送cookie。第二点,简单跨域中,浏览器的请求直接发送给服务器,服务器返回是否支持跨域(即是否header加origin),那么简单跨域究竟是请求了服务端几次?如果是1次,那么如果服务端不支持跨域,即没有设置allow,还会不会继续走下去,会不会继续request得到结果后放入response?就是不论跨域不跨域服务器是否都会执行这个request对应的计算。因为所有的设置header都是给浏览器告知的,和服务端限制无关。(待验证)
验证:
即使服务端没有设置允许跨域,当客户端请求过来时,服务端仍旧完整执行了请求并返回,只是客户端没有接收。服务端需要做点工作
针对上述两种跨域。server A需要写一个filter。
Filter:
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)
throws ServletException,IOException {
URL requestURL = new URL(request.getRequestURL().toString());
String hostName = requestURL.getHost();
String origin = request.getHeader("Origin");
int index = hostName.indexOf(".");
if(index > -1) {
String domainHost = hostName.substring(index,hostName.length());
if(!StringUtils.isEmpty(origin) && origin.contains(domainHost)) {
response.addHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS");
response.addHeader("Access-Control-Allow-Origin",origin);
response.addHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Max-Age","3600");
response.addHeader("Access-Control-Allow-Headers","Content-Type,Cookie," +
"Accept-Encoding,User-Agent," +
"Host,Referer," +
"X-Requested-With,Accept," +
"Accept-Language,Cache-Control,Connection");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
// CORS "pre-flight" request
response.setStatus(200);
return;
}
}
}
filterChain.doFilter(request,response);
}
}
上述filter是为了同一个domain下,不同子域名可以跨域访问,而其他domain则不可以,因为我们需要共享cookie,所以设置 Access-Control-Allow-Credentials 为 true . 如果设置为 false 则不接受cookie。
客户端,即server B如果想要发送cookie则需要设置 withCredentials 为 true .
注意:针对非简单跨域的时候发送 options 请求,服务端A需要告诉浏览器是否支持跨域即可,不要往下走了,不然到指定的requestMapping发现不支持这个方法就会很尴尬了,所以直接返回。
下面针对简单跨域和非简单跨域做测试:
<script src="jquery-1.11.3.js">
<input type="button" value="GET_Default" onclick="testGetDefault()">
<input type="button" value="GET_JSON" onclick="testGetJSON()">
<input type="button" value="POST_Default" onclick="testPostDefault()">
<input type="button" value="POST_JSON" onclick="testPostJson()">
<input type="button" value="PUT" onclick="testPUT()">