跨域

Posted by Rimin on 2019-04-18

什么是跨域?

当前访问的服务器所返回的页面,页面内的JS访问另一个服务器的资源的时候,出现跨域。(本地资源打开的页面去访问服务器,也是跨域)

如何判断跨域?

  1. 协议是否相同
  2. 域名是否相同
  3. 端口号是否相同

举例:http://www.example.com/dir/page.html,
这个url的协议:http,域名为:example.com,顶级域名:.com。主机名:www (也是三级域名)。端口是80(默认端口可以省略),它的同源情况如下:

⚠️ 注意:域名和域名对应ip算跨域

⚠️ 注意:引入外链资源JS、IMG的时候没有跨域限制,Iframe也可以直接引入跨域页面,但无权限访问iframe

同源策略

  • 来源:

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。

浏览器的同源策略限制了来自不同源的“document”或脚本,对当前“document”读取或设置某些属性。注意: 对于当前页面来说,页面内存放JavaScript文件的域并不重要,重要的是加载JavaScript页面所在的域是什么。

比如:a.com通过加载一下代码:

1
2
> <script src=http://b.com/b.js ></script>
>

加载b.com上的b.js是运行在a.com页面上的,因此对于当前打开的页面(a.com页面)来说,b.js的Origin就应该是a.com而非b.com

在浏览器中,<script><img><iframe><link>等标签都可以跨域加载资源,而不受同源策略的限制。这些带“src”属性的标签每次加载时,实际上是由浏览器发起了一次GET请求。不同于XMLHttpRequest的是,通过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读、写返回的内容。

在HTML5中,有些元素提供了支持CORS(Cross-Origin Resource Sharing)(跨域资源共享)的属性,这些元素包括<img><video><script>等,而提供的属性名就是crossOrigin属性。

  • 目的

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。

  • 限制行为

(1) Cookie、LocalStorage 和 IndexDB 无法读取。

(2) DOM 无法获得。

(3) AJAX 请求不能发送。

实现跨域的几种方法

iframe

如果两个网页不同源,就无法拿到对方的DOM,也无法进行通信。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

解决方法:

  1. document.domain(如果两个窗口一级域名相同,只是二级域名不同)
  2. 片段识别符(fragment identifier)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//父窗口可以把信息,写入子窗口的片段标识符
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

//子窗口通过监听hashchange事件得到通知
window.onhashchange = checkMessage;

function checkMessage() {
var message = window.location.hash;
// ...
}

//同样的,子窗口也可以改变父窗口的片段标识符
parent.location.href= target + "#" + hash;
  1. window.name
    无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。window.name容量很大,可以放置非常长的字符串.(ps:个人感觉也是一个小tip)

  2. window.postMessage (MessageChannel)

1
2
var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');
1
2
3
4
5
//父窗口和子窗口都可以通过message事件,监听对方的消息。

window.addEventListener('message', function(e) {
console.log(e.data);
},false);

AJAX

1. JSONP
  • 特点:利用script标签实现跨域,兼容性好。
  • 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// jsonp
function jsonp(url, jsonpCallback){
let script = document.createElement('script');
url = handleUrl(url)
script.src = url

window[jsonpCallback] = function(res){
document.body.removeChild(script)
delete window[jsonpCallback]
console.log(res)
}
}

/**
*@param {String} url
*@param {Object} data
**/
function handleUrl(url, data){
var keys = Object.keys(data);
let params = keys.reduce((pre, cur, index) => {
const value = data[cur];
const slipt = index === keys.length - 1? '' : '&'
return `${pre}${cur}=${value}${slipt}`
}, '')
return `${url}?${params}`
}

url返回的即是将数据传入jsonpCallback并调用执行的脚本。而script标签会让它自动执行。

2. WebSocket
  • WebSocket 教程—阮一峰 (websocket没有同源限制,客户端可以与任意服务器通信,顺便说一下它最大的特点,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。)
3. CORS

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。

注意:普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置

1
2
3
4
5
// 前端设置是否带cookie
xhr.withCredentials = true;

// 后端设置是否携带cookie
response.setHeader("Access-Control-Allow-Credentials", "true");

nginx代理跨域,node开启服务作为代理跨域

跨域原理: 同源策略只是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。