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 相关文章推荐
jQuery Tools tab(幻灯片)
Jul 14 Javascript
jqGrid随窗口大小变化自适应大小的示例代码
Dec 28 Javascript
JQuery动画animate的stop方法使用详解
May 09 Javascript
javascript中使用new与不使用实例化对象的区别
Jun 22 Javascript
Vuex之理解Store的用法
Apr 19 Javascript
基于rem的移动端响应式适配方案(详解)
Jul 07 Javascript
微信小程序实现人脸识别
May 25 Javascript
Vue.js 父子组件通信的十种方式
Oct 30 Javascript
Vue自定义指令上报Google Analytics事件统计的方法
Feb 25 Javascript
JavaScript实现预览本地上传图片功能完整示例
Mar 08 Javascript
Jquery $.map使用方法实例详解
Sep 01 jQuery
vue仿携程轮播图效果(滑动轮播,下方高度自适应)
Feb 11 Vue.js
微信小程序 五星评分的实现实例
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高级编程-函数-郑阿奇
2011/07/04 PHP
php删除字符串末尾子字符,删除开始字符,删除两端字符(实现代码)
2013/06/27 PHP
destoon网站转移服务器后搜索汉字出现乱码的解决方法
2014/06/21 PHP
3款值得推荐的微信开发开源框架
2014/10/28 PHP
Thinkphp多文件上传实现方法
2014/10/31 PHP
浅谈PHP的数据库接口和技术
2016/12/09 PHP
PHP用户注册邮件激活账户的实现代码
2017/05/31 PHP
使用Laravel中的查询构造器实现增删改查功能
2019/09/03 PHP
jquery(live)中File input的change方法只起一次作用的解决办法
2011/10/21 Javascript
javascript错误的认识不用关心内存管理
2012/12/15 Javascript
文件编码导致jquery失效的解决方法
2013/06/26 Javascript
js判断一个字符串是否包含一个子串的方法
2015/01/26 Javascript
JS实现兼容各浏览器解析XML文档数据的方法
2015/06/01 Javascript
js判断浏览器类型及设备(移动页面开发)
2015/07/30 Javascript
数据分析软件之FineReport教程:[5]参数界面JS(全)
2015/08/13 Javascript
jquery自定义插件——window的实现【示例代码】
2016/05/06 Javascript
第四篇Bootstrap网格系统偏移列和嵌套列
2016/06/21 Javascript
Js遍历键值对形式对象或Map形式的方法
2016/08/08 Javascript
js+css3制作时钟特效
2016/10/16 Javascript
AngularJS改变元素显示状态
2017/04/20 Javascript
配置node服务器并且链接微信公众号接口配置步骤详解
2019/06/21 Javascript
Node.js API详解之 string_decoder用法实例分析
2020/04/29 Javascript
Vue 样式切换及三元判断样式关联操作
2020/08/09 Javascript
Python实现栈的方法
2015/05/26 Python
Python IDE PyCharm的基本快捷键和配置简介
2015/11/04 Python
Python基础语法(Python基础知识点)
2016/02/28 Python
PyTorch线性回归和逻辑回归实战示例
2018/05/22 Python
浅谈python输出列表元素的所有排列形式
2020/02/26 Python
Django serializer优化类视图的实现示例
2020/07/16 Python
Origins悦木之源香港官网:雅诗兰黛集团高端植物护肤品牌
2018/03/21 全球购物
大学生职业生涯设计书
2014/01/02 职场文书
本科生自荐信
2014/06/18 职场文书
局机关干部群众路线个人对照检查材料思想汇报
2014/10/05 职场文书
2015年社区卫生工作总结
2015/04/21 职场文书
讲座开场白台词和结束语
2015/05/29 职场文书
排查Tomcat进程假死的问题
2022/05/06 Servers