Skip to content

Latest commit

 

History

History
134 lines (100 loc) · 5.48 KB

02-常见跨域方法.md

File metadata and controls

134 lines (100 loc) · 5.48 KB

总结了开发种常见的几种跨域方法。

Demo地址: https://github.com/FatDong1/cross-origin

Q: 为什么会出现跨域

A: 出于浏览器的同源策略限制,浏览器会拒绝跨域请求。

注:严格的说,浏览器并不是拒绝所有的跨域请求,实际上拒绝的是跨域的读操作。浏览器的同源限制策略是这样执行的:

通常浏览器允许进行跨域写操作(Cross-origin writes),如链接,重定向; 通常浏览器允许跨域资源嵌入(Cross-origin embedding),如 img、script 标签; 通常浏览器不允许跨域读操作(Cross-origin reads)。

Q: 什么情况下算跨域

A: 非同源请求,均为跨域。 名词解释:同源 —— 如果两个页面拥有相同的协议(protocol),端口(port)和主机(host),那么这两个页面就属于同一个源(origin)。

主机URL:http://www.example.com 是否跨域 原因
http://www.example.com/a.html 同源
https://www.example.com/a.html 协议不同
http://www.example.com:8080/a.html 端口不同
http://example.com/a.html 主机不同(二级域名不同)

Q:如何跨域

1. CORS(跨域资源共享)

此方法需要前后端配合

MDN 关于跨域资源共享的解释

简单点来说就是当服务器在返回响应时,在其响应报文头的 Access-Control-Allow-Origin 中返回允许访问的域,或者返回 * 表示允许任意外域访问。

请求报文:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example

请求报文的第10行:Origin: foo.example 表明该请求来源于 foo.exmaple。

响应报文:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]

响应报文的第4行:Access-Control-Allow-Origin: * 表明该资源可以被任意外域访问。

2. JSONP(JSON with padding)

此方法需要前后端配合

原理:web 页面上调用 js 文件不受跨域影响(Cross-origin embedding)。 前端在页面上请求 js 文件,并在查询字符串 callback 中带上方法名。

<script src="http://localhost:3001?callback=myFunction"></script>

后端获取到方法名之后,返回一个函数调用,函数的参数就是我们想要的数据。

myFunction({'message': 'hello world from JSONP!🙃'});

3. postMessage

适用于同一页面的不同窗体(iframe)。 语法:otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow:
其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。

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

targetOrigin:
通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串" * "(表示无限制)或者一个 URI。
在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;
只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;
例如,当用 postMessage 传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的 origin 属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的 targetOrigin,而不是 *。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。

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

例:

<body>
<div id="father">
    <p>这里3000端口</p>
    <input type="text"/>
    <button>发送信息给 3001 端口</button>
</div>
<iframe id="child" src="http://localhost:3001"></iframe>
// 从 3000 端口向 3001 端口推送消息
childFrame.postMessage('消息内容', 'http://localhost:3001');
//从 3001 端口接收消息
window.addEventListener('message', receiveMessage, false);

function receiveMessage (event) {
    if (event.origin !== 'http://localhost:3000') {
        return false
    }
    // 从 3000 端口向 3001 端口推送消息
    parentWindow.postMessage('我收到你的消息了', 'http://localhost:3000/');
}