为什么会跨域
- 浏览器的同源策略(MDN:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy)
- URL:协议://域名:端口/路径/文件名?参数1&参数2#锚
- 如果两个页面的协议、域名(主机)、端口相同,则两个页面同源
- 目的:限制从这个源加载的文档或脚本与来自另一个源的资源进行交互,隔离潜在恶意文件。
- 跨域举个栗子
- https://domain-a.com的网页用XMLHttpRequest请求https://domain-b.com/data.json
跨域解决方案
document.domain
- 适用于主域相同,子域不同的
- 脚本可以将 document.domain 的值设置为其当前域或其当前域的父域。如果将其设置为其当前域的父域,则这个较短的父域将用于后续源检查。
- 例如:假设 http://store.company.com/dir/other.html 文档中的一个脚本执行以下语句,则页面将会成功地通过对 http://company.com/dir/page.html 的同源检测(假设http://company.com/dir/page.html 将其 document.domain 设置为“company.com”,以表明它希望允许这样做)
document.domain = "company.com"
- 然而,company.com 不能设置 document.domain 为 othercompany.com,因为它不是 company.com 的父域。
- 端口号是由浏览器另行检查的。任何对document.domain的赋值操作,包括 document.domain = document.domain 都会导致端口号被重写为 null 。因此 company.com:8080 不能仅通过设置 document.domain = "company.com" 来与company.com 通信。必须在他们双方中都进行赋值,以确保端口号都为 null 。
- 例如:假设 http://store.company.com/dir/other.html 文档中的一个脚本执行以下语句,则页面将会成功地通过对 http://company.com/dir/page.html 的同源检测(假设http://company.com/dir/page.html 将其 document.domain 设置为“company.com”,以表明它希望允许这样做)
通过html元素(标签)嵌入跨入资源
- <script src="..."> 标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
- <link rel="stylesheet" href="..."> 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type 消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
- <img>嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...
- <video> 和 <audio>嵌入多媒体资源。
- <object>, <embed> 和 <applet> 的插件。
- @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
- <frame> 和 <iframe> 载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。
JSONP
- JSON with Padding
- 原理:目标文件回调本地页面的方法,并传入参数(数据)
- 客户端将处理数据的回调函数作为参数拼接在url后面传给服务端,服务端用回调函数包裹数据的js文件(多为json后缀)返回给客户端,js代码在客户端页面中执行
- 优点:简单实用,可兼容性高; 缺点:只支持Get请求
- 实现
静态 page.html <script> function getData(data){ console.log(data); } </script> <script src="serverURL/server.js"></scipt> server.js getData({name: "Tom", age: 18});
动态:服务端如何知道回调方法名呢 <script type="text/javascript"> function showData(data){ console.log(data); } var _script = document.createElement('script'); var _url = "http://server.com/dealData.php?callback=showData"; // 告诉服务端,客户端处理数据的回调函数是什么,服务端拿到参数后,解析出回调函数名,用函数包裹待返回的数据后返回给客户端 _script.setAttribute('src', url); document.getElementByTagName('head')[0].appendChild(_script); // 将动态创建的script脚本放入页面合适的位置 </script>
- 参考博文:https://blog.csdn.net/hansexploration/article/details/80314948
CORS
MDN:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
- Cross-Origin Resource Sharing —— 跨源资源共享,它是W3C标准,是解决跨源AJAX请求的官方解决方案
- 适用场景:
- 前文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
- Web 字体 (CSS 中通过 @font-face 使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。
- WebGL 贴图
- 使用 drawImage 将 Images/video 画面绘制到 canvas
- 原理:跨域资源共享标准新增了一组 HTTP 首部字段Access-Control-*
- Access-Control-Allow-Origin:允许服务器声明哪些源站通过浏览器有权限访问哪些资源
Access-Control-Allow-Origin: <origin> | * <origin>例:http://mozilla.com
- 普通跨域请求,只需要后端设置Access-Control-Allow-Origin授权网站发送请求即可
- 带cookies的跨域请求,需要前后端都设置
- 前端
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
- 服务端
Access-Control-Allow-Credentials: true
- 如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
- 如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
- 前端
- 参考博文:https://blog.csdn.net/qq_38128179/article/details/84956552
跨源脚本API访问
- 来自MDN
Javascript的APIs中,如 iframe.contentWindow, window.parent, window.open 和 window.opener 允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对 Window 和 Location对象的访问添加限制。
允许访问window的属性 | 允许访问Location的属性 |
---|---|
方法: window.blur window.close window.focus window.postMessage 属性: window.closed(只读) window.frames(只读) window.length(只读) window.location(读/写) window.opener(只读) window.parent(只读) window.self(只读) window.top(只读) window.window(只读) |
方法: location.replace 属性: URLUtils.href(只写) |
跨源数据存储访问
- 来自MDN
存储在浏览器中的数据,如localStorage和IndexedDB,以源进行分割。每个源都拥有自己单独的存储空间,一个源中的Javascript脚本不能对属于其它源的数据进行读写操作。
Cookies 使用不同的源定义方式。一个页面可以为本域和任何父域设置cookie,只要是父域不是公共后缀(public suffix)即可。Firefox 和 Chrome 使用 Public Suffix List 决定一个域是否是一个公共后缀(public suffix)。Internet Explorer使用其自己的内部方法来确定域是否是公共后缀。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains) 访问 cookie。设置 cookie 时,你可以使用Domain,Path,Secure,和Http-Only标记来限定其访问性。读取 cookie 时,不会知晓它的出处。 即使您仅使用安全的https连接,您看到的任何cookie都可能使用不安全的连接进行设置。