同源策略 Same Origin Policy

日常开发中最常与网络打交道,那关于浏览器的同源策略和跨域相关的知识是该整理一下了。

首先需要明确的是,同源策略是浏览器的安全策略,由于存在这个策略,我们才需要对各种跨域需求进行处理。

同源策略的主要目的是为了保护用户的信息安全。

什么是同源

同源的含义其实比较好理解,实际上就是三点

  • 协议相同
  • 域名相同
  • 端口相同
url 说明 是否同源
http://www.test.com https://www.test.com 同一域名,不同协议 不同源
http://www.test.com https://www.test1.com 不同域名 不同源
http://www.test.com:8080 https://www.test.com:8081 同一域名,不同端口 不同源
https://www.test.com http://192.168.1.1 域名和域名对应的 ip 不同源

同源限制了什么

1、Cookie、localStorage 和 IndexDB 无法读取

2、DOM 无法获得

3、AJAX 请求不能发送

规避方法

1、读取 Cookie

cookie 的读取与其他属性有些不同。因为 cookie 的可见性是由 domain 属性和 path 属性决定的,所以他其实不受协议和端口的限制。只要是同一个 domain 和可见 path 下的 cookie,都能读取到。

可见 path

  • / 只能读取 / 下的 cookie
  • /test 可以读取 / 和 /test 下的 cookie

由于 cookie 的读取本身具有局限性,所以跨域后的规避可以这样操作

  • 前端读取不同 domain 的 cookie,可以把通过设置 document.domain 来读取相应 cookie。当然,你设置的 domain 必须是你当前 domain 的上一级域名,否则会报如下的错误。

    Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'test' is not a suffix of 'localhost'.
  • 前端和服务器端设置 cookie 时指定可读的 domian 和 path,这个 domain 也遵循上面的规则,只能设置当前 domain 或 domain 上一级的域名,否则会设置失败。本地测试的现象是没有报错,但是也没有设置成功。

2、读取 localStorage、IndexedDB 和 DOM

localStorage、IndexedDB 和 DOM 是实实在在受同源策略限制的,协议、域名和端口任意一项不相同就无法读取。

其实对于他们的读取可以简化成不同源的两个页面如何通信。

本身 localStorage 和 IndexedDB 的出现就是为了让我们能够更好地存储数据,而获取 DOM 大多情况也是为了获取 DOM 中的各种数据。泛化到业务场景中,也就是传递数据的事儿了。

目前可以通过以下几种方式来进行通信。

  • window.name

    使用 window.name 的原理是同源 iframe 可以读取 contentWindow。具体操作如下

    1、a 页面先载入一个不同源的 iframe 页面 b

    2、在 b 页面 中修改 window.name 为需要的传递的数据

    3、a 页面中修改这个 iframe 的 src 为同源的页面 c

    4、a 页面获取之前设置的 window.name

    按照这样的方式进行操作就能获取到子页面中的数据。如果子页面想要获取父页面中的数据,可以将 1、3 步骤换一下,2 步骤改成父页面直接修改 iframe.contentWindow.name 为需要传递的数据即可。

    因为是通过 name 属性来传递参数,所以可传递的数据量很大,基本就是字符串长度的最大值。

    //记录 iframe onload 事件的加载次数
    var state = 0;
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = "http://sub.test.com/test/test.html";
    // onload 事件会触发2次,第1次加载跨域页,在跨域页中将所需要的数据赋给 window.name
    iframe.onload = function () {
    if (state === 1) {
    // 第2次 onload 成功后,读取同域 window.name 中数据
    var data = iframe.contentWindow.name;
    } else if (state === 0) {
    // 第1次onload(跨域页)成功后,切换到同域代理页面
    iframe.contentWindow.location = "./testB.html";
    state = 1;
    }
    };
    document.body.appendChild(iframe);
  • fragment identifier 片段标识符

    fragment identifier 其实就是 url # 后面的部分。常写 SPA 应用的小伙伴应该会对他很熟悉,因为 hash 模式的 router 就是基于他实现的。修改嵌入的 iframe 的 src 为 url#需要传递的数据,iframe 页面就能通过监听 hashchange 事件获得传递的数据。如果是子页面向父页面传递数据要多加一步,得先让父页面把自己当前的 url 传递给子页面,然后子页面去修改父页面的 href。

    使用这个方法传递数据的限制暂时还未测试,猜测应该会和浏览器限制 url 的长度有关。

    //父->子 a页面
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = url;
    setTimeout(function () {
    iframe.src = url + "#a=111";
    }, 1000);
    document.body.appendChild(iframe); //父->子 b页面
    window.onhashchange = function () {
    alert(window.location.hash)
    }
    //子->父 a页面
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = url;
    setTimeout(function () {
    var selfurl = document.location.href;
    iframe.src = url + "#" + selfurl;
    }, 1000);
    document.body.appendChild(iframe);
    window.onhashchange = function () {
    alert("from son" + window.location.hash);
    }; //子->父 b页面
    window.onhashchange = function () {
    let target = window.location.hash.slice(1);
    window.parent.parent.location.href = target + "#111aaaa"
    }
  • postMessage

    严格意义上来说上面两种方法都是对跨域页面获取数据的破解,postMessage 才是正统的非同源页面之间传递数据的方法。

    window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

    postMessage 是新增的 API,使用前记得查看一下兼容性。

    使用上需要注意的就是调用 postMessage 和监听 message 事件的的主体是同一个。也就是说子页面向父页面发送消息时,需要获取 window.parent 去调用 postMessage。父页面向子页面发送消息时,可以直接获取 iframe,然后调用 postMessage。

    postMessage((message, targetOrigin, [transfer]);

    message

    将要发送到其他 window 的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。

    targetOrigin

    通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串""(表示无限制)或者一个 URI。

    在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。

    这个机制用来控制消息可以发送到哪些窗口;例如,当用 postMessage 传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的 origin 属性完全一致,来防止密码被恶意的第三方截获。

    如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的 targetOrigin,而不是
    。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。

    transfer 可选

    是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权

    下面这个示例是子页面向父页面发送消息。

    // a 页面
    window.addEventListener("message", function (event) {
    console.log(event);
    }, false); // b 页面
    var b = "bbb";
    window.parent.postMessage({
    b
    }, "*")
  • 另外特别需要注意的是关于 DOM 的获取,如果只是两个不同子域的页面,将 document.domain 设置为同一主域就可以读取相应数据。

    //a 页面 main.test.com
    var a = "aaa";
    // domain 设置为主域
    document.domain = "test.com";
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = "sub.test.com";
    document.body.appendChild(iframe);
    iframe.onload = function(){
    // 可以获取到变量
    var data = iframe.contentWindow.b
    // 可以获取到 DOM
    var dom = iframe.contentWindow.document.body;
    } //b 页面 sub.test.com
    var b = "bbb";
    // domain 设置为主域
    document.domain = "test.com";

3、AJAX 请求

AJAX 请求跨域日常使用比较多,常用的方法有以下几种

  • JSONP

    这个方法是向服务器请求的时候,在 url 后面写上 callback 方法的名字,请求返回实际上是返回了一个 调用 callback 方法的 js 文件。需要返回的参数也就在调用的时候传进去了。

    所以局限也很明确,只支持 get 方法。

  • 使用代理服务器

    所有的请求先发送给这个代理服务器,由这个代理服务器去请求实际的接口,再把需要的数据返回。

    (这个方式就可以真切地体会到,同源策略只是浏览器的一种安全策略)

  • 使用 CORS Cross-Origin Resource Sharing 跨域资源共享

    目前基本上所有的浏览器都支持 CORS,所以只需要服务器端进行处理。对于前端来说,请求和同源的 AJAX 请求是一致的。

    前端发送请求的时候浏览器会自动带上 origin 字段,服务器端去判断这个 origin 是否是可接受的地址,在相应头中设置 Access-Control-Allow-Origin 字段的值。

    这样前端就能正常获取到数据啦。

    这里只对 CORS 做了一个简单介绍,详细的下次细说吧。

总结:

  • 读取跨域的 cookie 需要设置 path 和 domain
  • 不同源页面间通讯可以通过设置 window.name、location 的 hash 值和 postMessage 来实现。

    不难发现的是,设置 hash 和 postMessage 都是通过设置一个监听函数来实现的,所以我们是异步获取数据的。

    而设置 window.name 虽然是在 iframe.onload 事件中获取的,但是本质上是在等待 iframe 的加载,确保数据已经设置成功。

    这里还有值得思考的地方。

参考资料

浏览器同源政策及其规避方法

浏览器的同源策略

window.postMessage

跨源资源共享(CORS)

前端常见跨域解决方案(全)

Same Origin Policy 浏览器同源策略详解的更多相关文章

  1. [js]浏览器同源策略(same-origin policy)

    浏览器的同源策略 浏览器同源政策及其规避方法 什么是同源策略 A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源".所谓"同源"指的是" ...

  2. 七牛云存储Python SDK使用教程 - 上传策略详解

    文 七牛云存储Python SDK使用教程 - 上传策略详解 七牛云存储 python-sdk 七牛云存储教程 jemygraw 2015年01月04日发布 推荐 1 推荐 收藏 2 收藏,2.7k  ...

  3. jsonp突破浏览器同源策略

    jsonp突破浏览器同源策略 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  4. guava-retrying 源码解析(等待策略详解)

    一.等待策略相关类: 1.等待策略接口:WaitStrategy接口 该接口只有一个方法,就是返回尝试失败之后,下一次尝试之前的等待时间.long computeSleepTime(Attempt f ...

  5. django csrf_protect及浏览器同源策略

    1.django在检测post行为时会有诸多的限制. 为了防止跨域请求伪造安全 参考:http://www.qttc.net/201209211.html   https://www.cnblogs. ...

  6. 浏览器同源策略及Cookie的作用域

    from:https://blog.csdn.net/wang379275614/article/details/53333054 如题,本文主要介绍两方面内容:首先简单介绍浏览器的同源策略与其带来的 ...

  7. [转帖]Nginx服务器的六种负载均衡策略详解

    Nginx服务器的六种负载均衡策略详解 咔咔侃技术 2019-09-11 17:40:12 一.关于Nginx的负载均衡 在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独 ...

  8. Kubernetes 部署策略详解-转载学习

    Kubernetes 部署策略详解 参考:https://www.qikqiak.com/post/k8s-deployment-strategies/ 在Kubernetes中有几种不同的方式发布应 ...

  9. 踩坑录- Spring Boot - CORS 跨域 - 浏览器同源策略

    1.解决办法,创建一个过滤器,处理所有response响应头 import java.io.IOException; import javax.servlet.Filter; import javax ...

随机推荐

  1. python flask_sqlalchemy 多态 polymorphic 实现单表继承

    sqlalchemy 多态 polymorphic 实现单表继承 sqlaclchemy中的单表继续就是以一个模型类为基类,其他模型类继承基类,所有模型类的的数据都存一张表里面(也可以是多张,只不过基 ...

  2. sentinel流量控制和熔断降级执行流程之源码分析

    前言: sentinel是阿里针对服务流量控制.熔断降级的框架,如何使用官方都有很详细的文档,下载它的源码包 里面对各大主流框都做了适配按理,本系列文章目的 主要通过源码分析sentinel流量控制和 ...

  3. Jenkins开启丢弃旧的构建?你可要小心啊!

    玩Devops的小伙伴应该对Jenkins都有了解. Github上16.8k的Star的项目,1500+的构建.发布等自动化插件可供选择,事实上的业界CICD标准领导者. JFrog.Coding等 ...

  4. Java中详述线程间协作

    线程协作 首先引入一段代码: package 线程间数据共享; import java.util.Date; public class Watch { private static String ti ...

  5. UI自动化实战进阶后续

    前言 最近几天因为回老家的缘故,暂时没空学习和记录,好不容易抽空那就赶紧开始后面的实战. 前面我们已经基本完成了测试的框架,并且也有了PO设计模式,后面我们还缺少什么呢?做为自动化测试最主要的测试报告 ...

  6. Codeforces Round #672 (Div. 2) D. Rescue Nibel!(排序)

    题目链接:https://codeforces.com/contest/1420/problem/D 前言 之前写过这场比赛的题解,不过感觉这一题还可以再单独拿出来好好捋一下思路. 题意 给出 $n$ ...

  7. Codeforces Round #646 (Div. 2) E. Tree Shuffling(树上dp)

    题目链接:https://codeforces.com/contest/1363/problem/E 题意 有一棵 $n$ 个结点,根为结点 $1$ 的树,每个结点有一个选取代价 $a_i$,当前 $ ...

  8. C#之Dispose

    前言 谈到Dispose,首先需要理解C#的资源 资源类型 托管资源:由CLR创建和释放 非托管资源:资源的创建和释放不由CLR管理.比如IO.网络连接.数据库连接等等.需要开发人员手动释放. 如何释 ...

  9. 通过js正则表达式实例学习正则表达式基本语法

    正则表达式又叫规则表达式,一般用来检查字符串中是否有与规则相匹配的子串,达到可以对匹配的子串进行提取.删除.替换等操作的目的.先了解有哪些方法可以使用正则对字符串来实现这些操作: RegExpObje ...

  10. 4.安装fluentd用于收集集群内部应用日志

    作者 微信:tangy8080 电子邮箱:914661180@qq.com 更新时间:2019-06-13 11:02:14 星期四 欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程 ...