JS中的跨域问题

Posted 提升自己,才有选择的能力和勇气

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS中的跨域问题相关的知识,希望对你有一定的参考价值。

1、跨域的概念

1.1、同源策略(域名、协议、端口)

浏览器安全的基石是"同源政策",同源策略指的是域名、协议、端口号都相同,只要 协议,域名,端口有任何一个的不同,就被当作是跨域。

为了保证用户信息的安全,防止恶意的网站窃取数据,目前,所有浏览器都实行了同源策略,要求域名、协议、端口必须都相同才属于同源,只有同源才可以访问其他页面的对象,否则将受到以下限制:

  1. Cookie、LocalStorage 和 IndexDB 无法读取。
  2. DOM 无法获得。
  3.  AJAX 请求不能发送。

 

举例来说,http://www.example.com/dir/page.html这个网址,协议是http://,域名是www.example.com,端口是80(默认端口可以省略)。它的同源情况如下。

  • http://www.example.com/dir2/other.html:同源
  • http://example.com/dir/other.html:不同源(域名不同)
  • http://v2.www.example.com/dir/other.html:不同源(域名不同)
  • http://www.example.com:81/dir/other.html:不同源(端口不同)

 

URL由协议+主机名/域名+端口+路径+查询字符串+信息片段组成。

http://mail.163.com:8000/index.html
http://:是协议;mail:是服务器名;163.com:是域名;mail.163.com:这个是主机名(网站名),由服务器名+域名组成;8000:是端口号

 

1.2、为什么要限制跨域访问

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?

很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。

由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。

例子:比如一个黑客,他利用 iframe 把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名和密码登录时,如果没有同源限制,他的页面就可以通过 javascript读取到你的表单中输入的内容,这样用户名和密码就轻松到手了。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。比如有时公司内部有多个不同的子域,比如一个是location.company.com ,而应用是放在app.company.com , 这时想从 app.company.com去访问 location.company.com 的资源就属于跨域。

 

1.3、常见的出现跨域场景

  1. 两个跨域网页通过 iframe 嵌套。比如:http://www.baidu.com 通过 iframe 嵌入了 http://www.google.com 网页,此时如果通过 baidu 或者通过 google 来访问对方的数据,比如 dom 元素,或是全局变量,都会产生跨域问题
  2. 通过 window.open(url) 方法打开一个跨域网站。比如 http://www.baidu.com 通过 window.open() 方法打开了 http://www.google.com 网站,google 网站可以通过 widdow.opener 来获取 baidu 网站的引用,如果通过该引用来访问 baidu 的全局变量、dom元素等,就会产生跨域问题

 

1.4、跨域报错提示信息

跨域访问浏览器信息示例:

  • No \'Access-Control-Allow-Origin\' header is present on the requested resource. Origin \'http://localhost:9100\' is therefore not allowed access. The response had HTTP status code 400.

 

2、解决跨域

2.1、window.postMessage(msg, url)

HTML5为了解决跨域问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。

比如,父窗口 http://www.aa.com/a.html 需要和子窗口 http://www.bb.com/b.html 互发消息:

 

 

2.1、通过JSONP实现跨域

JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写,是应用 JSON 的一种新方法。JSONP 看起来与 JSON 差不多,只不过是被包含在函数调用中的 JSON, 比如:

callback({ "name": "Nicholas" });   //callback:回调函数名  JSONP可以看做就是一段以json为参数的函数调用代码

JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在浏览器页面中调用的函数。

JSONP 是通过<script>标签来使用的,使用时可以为src 属性指定一个跨域 URL。这里的<script>元素与<img>元素类似,都有能力不受限制地从其他域加载资源。因为 JSONP 是有效的 JavaScript 代码,所以在请求完成后,即在 JSONP 响应加载到页面中以后,就会立即执行。

 

JSONP的原理:通过script标签引入一个 js 文件,这个 js 文件载入成功后会自动执行在 src 参数中指定的回调函数,并且后端会设计把需要的 json 数据作为参数传入。所以 jsonp 是需要服务器端进行配合设计的,即后端需获得前端传过来的回调函数名,并把需要的json数据作为参数,然后把一段回调函数执行的代码返回,当前端加载到后端页面时将收到后端返回的那段代码,然后自动执行回调函数。

//前端代码
<script type="text/javascript">
    function dosomething(jsondata){
        console.log(\'数据:\', jsondata)
    }
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>

//后端代码
<?php
$callback = $_GET[\'callback\'];//获取前端传来的回调函数名
$data = array(\'a\',\'b\',\'c\');//这是要返回的数据
echo $callback.\'(\'.json_encode($data).\')\';//输出 JSONP ,前端拿到这个输出将会执行这段代码
?>

可以通过动态创建 script 标签来实现 JSONP,这样就不用在 html结构上添加额外的script标签了

<input type="text" name="number" id="number" value="" />
<p id="info"></p>
<input type="button" name="" id="btn" value="查询" />
<script type="text/javascript">
  var btn = document.getElementById("btn");
  var numbers = document.getElementById("number");
  var info = document.getElementById("info");
  btn.onclick = function () {
    var value = numbers.value;
    var script = document.createElement("script");
    script.src = "http://10.80.1.23/PHP/ajax/logistics/logistics.php?kuaidi_id=" + value + "&callback=show";
    document.body.appendChild(script);
  }

  function show(val) { // 插入数据
    info.innerText = "姓名:" + val.info + " 状态:" + val.status;
  }
</script>

JSONP的缺点:

(1)它只支持GET请求而不支持POST等其它类型的HTTP请求
(2)它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
(3)jsonp在调用失败的时候不会返回各种HTTP状态码。
(4)缺点是安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。

 

2.2、CORS实现跨域

CORS跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。

对于前端开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。实现CORS通信的关键是服务器端,只要服务器实现了CORS接口,就可以跨源通信。如果服务器端实现了CORS,在进行AJAX请求时,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求。

CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。只需要在后台中加上响应头来允许域请求!

//指定允许其他域名访问
\'Access-Control-Allow-Origin:*\'//或指定域
//响应类型
\'Access-Control-Allow-Methods:GET,POST\'
//响应头设置
\'Access-Control-Allow-Headers:x-requested-with,content-type\'
//前端代码,跟一般ajax访问无区别
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
  if (xhr.readyState == 4 && xhr.status == 200) {
    console.log(xhr.responseText)
  }
}
xhr.open(\'GET\', \'http://192.168.2.186:8081/KuayuAjax/server.php\', true);
xhr.send(null);

//后端代码
<?php
header("Content-Type:text/plain");
header("Access-Control-Allow-Origin:http://localhost:8081");//设置头部,不设置的话请求会被拒绝
echo \'{"src":"http://www.pinkbluecp.cn/face_alignment/img/picture.jpg"}\';
?>

 

3、JS跨域请求json文件

前端开发在进行调试时,经常会使用一些模拟数据来模拟后端接口,此时我们可以通过请求本地 json 文件来模拟请求后端接口。

如果JS直接请求本地 json 文件会提示跨域问题,此时我们可以通过 jsonp 来解决这个问题:

//被请求的name.json文件应该这么写:
successCallback({
    "status": \'success\',
    "data": [{
        "name": "mike"
    }]
})
<script>
    function successCallback(data) {
        console.log(\'模拟数据:\', data);
    }
</script>

<!-- json文件中的函数名必须跟script标签中的回调函数、JS中定义的回调函数  名称保持一致 -->
<!-- <script src="./name.json?cb=successCallback"></script> -->

<script>
    var scriptDom = document.createElement(\'script\');
    scriptDom.src = \'./name.json?cb=successCallback\';
    document.body.appendChild(scriptDom);
</script>

输出:

 

以上是关于JS中的跨域问题的主要内容,如果未能解决你的问题,请参考以下文章

JS中的跨域问题

JS 中的跨域请求

Vue.js学习—— 分别从前后端Nginx解决SpringBoot+vue.js项目中的跨域问题

Java开发中解决Js的跨域问题

跨域:跨域及解决方法

原生的js实现jsonp的跨域封装