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函数
Dec 22 Javascript
jQuery 过滤not()与filter()实例代码
May 10 Javascript
深入理解JavaScript系列(26):设计模式之构造函数模式详解
Mar 03 Javascript
JavaScript学习笔记之ES6数组方法
Mar 25 Javascript
前端性能优化及技巧
May 06 Javascript
JS实现批量上传文件并显示进度功能
Jun 27 Javascript
two.js之实现动画效果示例
Nov 06 Javascript
详解微信小程序开发(项目从零开始)
Jun 06 Javascript
小程序实现左滑删除效果
Jul 25 Javascript
微信小程序登录对接Django后端实现JWT方式验证登录详解
Jul 29 Javascript
微信小程序HTTP请求从0到1封装
Sep 09 Javascript
微信小程序整个页面的自动适应布局的实现
Jul 12 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 array_unique之后json_encode需要注意
2011/01/02 PHP
php生成缩略图填充白边(等比缩略图方案)
2013/12/25 PHP
PHP实现支持GET,POST,Multipart/form-data的HTTP请求类
2014/09/24 PHP
Laravel Memcached缓存驱动的配置与应用方法分析
2016/10/08 PHP
use jscript with List Proxy Server Information
2007/06/11 Javascript
js保留小数点后几位的写法
2014/01/03 Javascript
jQuery前端框架easyui使用Dialog时bug处理
2014/12/05 Javascript
jQuery简单实现彩色云标签效果示例
2016/08/01 Javascript
jquery select2的使用心得(推荐)
2016/12/04 Javascript
NodeJs的fs读写删除移动监听
2017/04/28 NodeJs
JS 学习总结之正则表达式的懒惰性和贪婪性
2017/07/03 Javascript
vue中appear的用法
2017/08/17 Javascript
小程序实现留言板
2018/11/02 Javascript
使用JavaScript解析URL的方法示例
2019/03/01 Javascript
Javascript异步流程控制之串行执行详解
2020/09/27 Javascript
[08:44]DOTA2发布会群星聚首 我们都是刀塔人
2014/03/21 DOTA
Python 字符串操作实现代码(截取/替换/查找/分割)
2013/06/08 Python
详解PyTorch批训练及优化器比较
2018/04/28 Python
Python多继承顺序实例分析
2018/05/26 Python
浅谈tensorflow中几个随机函数的用法
2018/07/27 Python
妙用itchat! python实现久坐提醒功能
2019/11/25 Python
pytorch AvgPool2d函数使用详解
2020/01/03 Python
在pycharm中关掉ipython console/PyDev操作
2020/06/09 Python
Python调用Redis的示例代码
2020/11/24 Python
CSS Houdini实现动态波浪纹效果
2019/07/30 HTML / CSS
HTML5-WebSocket实现聊天室示例
2016/12/15 HTML / CSS
HTML5地理定位实例
2014/10/15 HTML / CSS
HTML5制作表格样式
2016/11/15 HTML / CSS
Lungolivigno Fashion官网:高级时装在线购物
2020/10/17 全球购物
考试违纪检讨书
2014/02/02 职场文书
公司离职证明样本
2014/09/13 职场文书
高中生旷课检讨书
2014/10/08 职场文书
党的群众路线教育实践活动批评与自我批评发言稿
2014/10/16 职场文书
现场施工员岗位职责
2015/04/11 职场文书
导游词之安徽醉翁亭
2020/01/10 职场文书
tomcat下部署jenkins的方法
2022/05/06 Servers