Javascript实现跨域后台设置拦截的方法详解


Posted in Javascript onAugust 04, 2017

本文主要给大家介绍了关于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。

但由于跨域保护,请求失败:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'xxxxx' is therefore not allowed access.

修改host

首先本地配置两个指向127.0.0.1的host,方便互相跨域。

127.0.0.1 local.corstest.com.net 
127.0.0.1 cros.corstest.com.net

启动项目A,方便提供API。

至于项目B,测试跨域只要写个html静态页面即可。那么就写一个test.html,并通过一个工具发布:

browser-sync

安装

npm install -g browser-sync

本地启动一个test.html

browser-sync start --server --files "*.html" --host "cros.corstest.com.net" --port 3001

关于跨域CORS

ruanyifeng 的文章里说浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

其中同时满足一下2种标准即为简单跨域:

1) 请求方法是以下三种方法之一:

  • 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>
 <filter-name>cors</filter-name>
  <filter-class>com.test.filter.CorsFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>cors</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
</filter>

Filter:

public class CorsFilter extends OncePerRequestFilter {

 @Override
 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 .

//原生
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
//jquery
$.ajax({
 ...
 xhrFields: {
  withCredentials: true
 }
 ...
});

注意:针对非简单跨域的时候发送 options 请求,服务端A需要告诉浏览器是否支持跨域即可,不要往下走了,不然到指定的requestMapping发现不支持这个方法就会很尴尬了,所以直接返回。

下面针对简单跨域和非简单跨域做测试:

<!DOCTYPE html>
<html lang="en">

 <meta charset="UTF-8">
 <title>test</title>

 <script src="jquery-1.11.3.js"></script>
</head>
<body>

<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()">

<script>
 var getUrl = "https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759";
 var postUrl = "https://local.corstest.com.net:8443/contentmain/saveReservationDeposits.json?htid=759";

 function testGetDefault(){
  sendAjax("GET",getUrl, "json", "application/x-www-form-urlencoded");
 }
 function testGetJSON(){
  sendAjax("GET",getUrl, "json", "application/json; charset=utf-8");
 }
 function testPostDefault(){
  sendAjax("POST",postUrl, "json", "application/x-www-form-urlencoded");
 }

 function testPostJson(){
  sendAjax("POST",postUrl, "json", "application/json; charset=utf-8");
 }

 function testPUT(){
  sendAjax("PUT",postUrl, "json", "application/json; charset=utf-8");
 }

 
 function sendAjax(type, url, dataType, contentType){
  $.ajax( { 
   type: type,
   url: url,
   xhrFields: {
    withCredentials: true
   },
   dataType : dataType, // accept type
   contentType: contentType, //request type, default is application/x-www-form-urlencoded
   success: function(result){
    console.log(result);
   },
   error: function (xhr) {
    console.log(xhr);
   }
  });
 }


</script>
</body>
</html>

结果:

GET default:

只发送一个正常的get请求。

GET json:

先发送一个options如下:

General:
Request URL:https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759
Request Method:OPTIONS
Status Code:200 OK
Remote Address:127.0.0.1:8443

Response Headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, X-Requested-With, Accept, Accept-Language, Cache-Control, Connection
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:http://cros.corstest.com.net:3001
Content-Length:0
Date:Thu, 30 Mar 2017 12:47:44 GMT
Server:Apache-Coyote/1.1

Request Headers:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:GET
Connection:keep-alive
Host:local.corstest.com.net:8443
Origin:http://cros.corstest.com.net:3001
Referer:http://cros.corstest.com.net:3001/test.html
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

然后再发送正常的Get请求。

post default:

正常发送请求。

post json: 先发送一个options请求。然后再发送正常的请求。

其他同理,总之,非简单跨域会多发一次options请求来确认是否支持跨域,这时候服务端一定要返回支持跨域,并且直接返回即可。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
再谈javascript面向对象编程
Mar 18 Javascript
简单常用的幻灯片播放实现代码
Sep 25 Javascript
JavaScript判断表单中多选框checkbox选中个数的方法
Aug 17 Javascript
详解BootStrap中Affix控件的使用及保持布局的美观的方法
Jul 08 Javascript
同步异步动态引入js文件的几种方法总结
Sep 23 Javascript
原生JS京东轮播图代码
Mar 22 Javascript
Bootstrap布局之栅格系统学习笔记
May 04 Javascript
深入理解vue中的$set
Jun 01 Javascript
vue2.0 自定义组件的方法(vue组件的封装)
Jun 05 Javascript
简单两步使用node发送qq邮件的方法
Mar 01 Javascript
详解小程序设置缓存并且不覆盖原有数据
Apr 15 Javascript
Vue文本模糊匹配功能如何实现
Jul 30 Javascript
微信小程序 五星评分的实现实例
Aug 04 #Javascript
JavaScript中in和hasOwnProperty区别详解
Aug 04 #Javascript
JavaScript hasOwnProperty() 函数实例详解
Aug 04 #Javascript
微信小程序 websocket 实现SpringMVC+Spring+Mybatis
Aug 04 #Javascript
Angular实现响应式表单
Aug 04 #Javascript
JS 实现banner图片轮播效果(鼠标事件)
Aug 04 #Javascript
jQuery选取所有复选框被选中的值并用Ajax异步提交数据的实例
Aug 04 #jQuery
You might like
PHP+Mysql+jQuery实现发布微博程序 jQuery篇
2011/10/08 PHP
字符串长度函数strlen和mb_strlen的区别示例介绍
2014/09/09 PHP
关于PHP虚拟主机概念及如何选择稳定的PHP虚拟主机
2018/11/20 PHP
PHP中rename()函数的妙用讲解
2019/02/28 PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
2019/09/11 PHP
Array对象方法参考
2006/10/03 Javascript
在修改准备发的批量美化select+可修改select时,在非IE下发现了几个问题
2007/01/09 Javascript
js 加密压缩出现bug解决方案
2014/11/25 Javascript
AngularJS表达式讲解及示例代码
2016/08/16 Javascript
Jquery和Js获得元素标签名称的方法总结
2016/10/08 Javascript
微信小程序 教程之条件渲染
2016/10/18 Javascript
Angular2表单自定义验证器的实现
2016/10/19 Javascript
vue-cli项目中使用公用的提示弹层tips或加载loading组件实例详解
2018/05/28 Javascript
微信小程序实现选项卡效果
2018/11/06 Javascript
JS代码检查工具ESLint介绍与使用方法
2020/02/04 Javascript
Vue Render函数原理及代码实例解析
2020/07/30 Javascript
关于vue-cli3打包代码后白屏的解决方案
2020/09/02 Javascript
[33:09]完美世界DOTA2联赛循环赛 Forest vs DM BO2第二场 10.29
2020/10/29 DOTA
如何使用七牛Python SDK写一个同步脚本及使用教程
2015/08/23 Python
Python字符串格式化输出方法分析
2016/04/13 Python
解决Tensorflow安装成功,但在导入时报错的问题
2018/06/13 Python
python实现n个数中选出m个数的方法
2018/11/13 Python
浅析Python __name__ 是什么
2020/07/07 Python
Python连接Impala实现步骤解析
2020/08/04 Python
python使用re模块爬取豆瓣Top250电影
2020/10/20 Python
AmazeUI 等分网格的实现示例
2020/08/25 HTML / CSS
白俄罗斯女装和针织品网上商店:Presli.by
2019/10/13 全球购物
ASICS印度官方网站:日本专业运动品牌
2020/06/20 全球购物
我的网上商城创业计划书
2013/12/26 职场文书
幼儿园小班评语
2014/04/18 职场文书
给校长的建议书300字
2014/05/16 职场文书
2014最新实习证明模板
2014/10/02 职场文书
2015年党员发展工作总结
2015/05/13 职场文书
刑事申诉状范文
2015/05/20 职场文书
2019年大学生职业生涯规划书最新范文
2019/03/25 职场文书
nginx容器方式反向代理实战
2022/04/18 Servers