浅谈浏览器「同源策略」

什么是同源政策 (Same Origin Policy)

「同源策略」指只有相同来源的的资源才能交互,限制跨来源资源的交互。
这是浏览器一个用于隔离潜在恶意文件的重要安全机制。
协议、域名以及端口三个完全一致才属于同源,其中一个不一致即非同源。

如果非同源,则有三种行为会受到限制。

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

2) DOM 无法获得。

3) XMLHttpRequest 或是 Fetch API 请求不能发送。

如何进行跨域通信

因为同源策略的限制,我们无法跨域通信。但在实际场景中我们可能有跨域通信的需求,这就需要我们去解决跨域通信问题了。

iframe

2-1-1 hash(片段标识符)

任一带#的URL称为片段URL。#左边部分是浏览器可以下载的资源,#右边部分称为片段标识符,表示资源内的某一位置。hash不会随HTTP请求发送,修改hash值不会重新加载页面。

// 比如
`http://example.com/x.html#fragment`
// #fragment 就是片段标识符

// 如何使用
// 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B
// 在父窗口A中伪代码如下:
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'data';

// 在子窗口B中的伪代码如下
window.onhashchange = function () {
    var data = window.location.hash;
};

2-1-2 使用 window.name

浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。

// 场景是父窗口 A 通过iframe或frame嵌入了跨域的页面 B
<iframe src="B.html" ></iframe>
var ifra = document.querySelector('iframe')

// B 中将信息写入window.name属性。
window.name = JSON.stringify(data)

// 接着,父窗口 A 的iframe窗口跳回一个与主窗口 A 同域的网址。
// 最好是空页面 C ,减少加载资源。
ifra.contentWindow.location = '与主窗口 A 同域的网址';

// 父窗口 A 就可以读取子窗口的window.name。
var data = ifra.contentWindow.name;

2-1-3 使用window.postMessage

// 窗口A(http://A.com)向跨域的窗口B(http://B.com)发送信息
// 场景是父窗口 A 通过iframe或frame嵌入了跨域的页面 B

// 窗口A
window.postMessage('data...', 'http://B.com');

// 在窗口B中监听
window.addEventListener('message', function (event) {
    if (event.origin !== 'http://A.com') return;
    console.log(event.origin);
    console.log(event.source);
    console.log(event.data);
}, false);

// 反之亦然

XMLHttpRequest/Fetch

同源政策限制了XMLHttpRequest 和 Fetch Api,此时前后端如何进行交互?

2-2-1 JSONP

JSONP是利用script标签src属性不受同源策略限制,可以跨域引用文件的能力。
具体步骤:全局环境下声明一个函数,动态创建script标签,src指定要访问的接口并指定callback名称与声明的函数名称一致,动态添加script标签,利用src属性发送跨域请求。

function createScript (url, charset) {
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    charset && script.setAttribute('charset', charset);
    script.setAttribute('src', url);
    script.async = true;
    return script;
};

function jsonp (url, callbackName, onsuccess, onerror, charset) {
    window[callbackName] = function () {
        if (onsuccess) {
            onsuccess(arguments[0]);
        }
    };
    var script = createScript(url + '&callback=' + callbackName, charset);
    script.onload = script.onreadystatechange = function () {
        if (!script.readyState || /loaded|complete/.test(script.readyState)) {
            script.onload = script.onreadystatechange = null;
            // 移除该script的 DOM 对象
            if (script.parentNode) {
                script.parentNode.removeChild(script);
            }
            // 删除函数或变量
            window[callbackName] = null;
        }
    };
    script.onerror = function () {
        if (onerror) {
            onerror();
        }
    };
    document.getElementsByTagName('head')[0].appendChild(script);
};

2-2-2 websocket

WebSocket是一种双向实时通信,没有同源限制,客户端可以与任意服务器通信。

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

2-2-3 CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。对于客户端而言,CORS通信与同源的AJAX通信没有差别,代码完全一样。只要服务器实现了CORS接口,就可以跨源通信。

参考资料

  1. http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
  2. http://www.ruanyifeng.com/blog/2016/04/cors.html
  3. https://medium.com/@jaydenlin/same-origin-policy-同源政策-一切安全的基礎-36432565a226

猜你喜欢

转载自www.cnblogs.com/horizon-jens/p/11996639.html
今日推荐