一、什么的是同源?
这是最初由Netscape提出的安全策略,这个策略是为了防止页面使用恶意的方法获取到另一个页面上的敏感信息。所有现代浏览器都会有这个策略。
如果请求数据时浏览器发现请求地址不同源,那么将会不允许这次的请求,报出一个错误。
上图是一个标准的url连接。
有一些还会带有查询(query)、信息片段(fragment),这里不做讨论;而所谓同源,就是通信协议、主机名、端口号必须相同。
例:
将下面的URL和https://www.kekeke.com:80/panda做对比
url | 结果 | 原因 |
---|---|---|
https://www.kekeke.com:80/cat | 同源 | 通信协议、主机名、端口号都相同 |
http://www.kekeke.com:80/cat | 不同源 | 通信协议不同(变成http) |
https://www.yyy.com:80/cat | 不同源 | 主机名不同 |
https://www.kekeke.com:2410/cat | 不同源 | 端口号不同 |
只要通信协议、主机名、端口号任意一个不相同,即是不同源。
二、什么是跨域
当我们想通过在自己的网站上获取另一个网站上的数据时,就会造成跨域。比如我在https://www.kekeke.com:80/panda的页面请求https://www.yyy.com:80/cat里的内容,就造成了跨域。浏览器就会不允许我们这么做
三、解决方法
1、JSONP
先来看看定义:JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说,https://www.kekeke.com:80/panda无法与https://www.yyy.com:80/cat的服务器作沟通的,但html标签中的<script></script>
是一个例外。
用法:
我们以请求一个豆瓣api接口https://api.douban.com/v2/book/1220562为例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<script>
function doubanCall(data){
console.log(data)
}
</script>
<script src='https://api.douban.com/v2/book/1220562?callback=doubanCall'></script>
</head>
<body>
</body>
</html>
返回数据如下:
当然doubanCall函数也被执行了:
jsonp的原理就是利用script标签可以不同源的特性,利用script标签存储返回的数据,然后通过JavaScript的代码来进行调用,当然这个方法也可以返回代码,而且还会被JavaScript解析引擎马上执行。
后面加了?callback=doubanCall是因为一般的jsonp也像其他请求一样,需要后端的配合,后端需要帮我们把数据传入指定的函数(比如callback)后进行返回,下面是我写的一个php小模拟,data表示要返回的数据
<?php
$fn = $_GET['callback'];
echo $fn.'(data)';
使用jQuery和angular等能在实际应用中帮我们更方便的使用jsonp,这个就不详解了。
2、使用window.name属性
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
例:
先看下第一个页面的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<script>
window.onload = function(){
window.name = '我是第一个页面的window.name属性'
}
</script>
</head>
<body>
<a href="https://www.taobao.com/">转到淘宝</a>
</body>
</html>
打开第一个页面点击a标签后,在淘宝上查询window.name会显示如下:
window.name在你保持这个窗口的时候,无论在这个窗口打开什么网页,都可以共享这个属性,但这个属性只能以字符串的形式出现,如果设置为其他类型,则会返回一个空字符串或者转换成字符串;而且它最多能保存2M左右的的数据,不同的浏览器可能有所不同。
3、使用CORS
新版本的XMLHttpRequest level 2对象,可以向不同域名的服务器发出HTTP请求。这叫做”跨域资源共享”(Cross-origin resource sharing,简称CORS),CORS就是使用自定义的http头部,让浏览器和服务器进行沟通。当然,想要使用这个功能,是浏览器必须支持,也需要后端的配合。
我们想用这方法来跨域则需要在修改下后端代码,这里以PHP为例:
//下面设置Access-Control-Allow-Origin属性,代表可以允许其他域名访问,
header('Access-Control-Allow-Origin:*');
$money = $_GET['money'];
if ($money > 100){
echo "服务器给数据了";
}else{
echo "没给钱,或钱给的不够";
}
如果需要指定某几个域名才能访问,则需要把Access-Control-Allow-Origin后面的星号(*)改为指定的网址就可以。
这是JS代码:
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log('有返回数据,跨域成功,嘤嘤嘤!_____' + xhr.responseText);
}
}
xhr.open('GET','http://localhost/ng-01/get.php?money=101',true);
xhr.send();
</script>
返回结果如下: