跨域这块是前后端分离必经的一条路, 这次好好整理一下关于跨域方面知识.
什么是跨域?
跨域是受同源策略的影响而导致的, 指一个源下的资源试图操作另一个源下的资源.
那么, 什么又是同源策略呢?
就是限制从一个源的资源如何与另一个源的资源交互, 用于隔离潜在的恶意文件, 保证文件的安全性. 所谓的同源是指: 协议/域名/端口 三者相同, 即使两个不同的域名指向同一个 IP 地址, 也非同源. 如果没有同源策略, 浏览器很容易受到 XSS/CSRF 等的攻击(XSS: 跨站脚本攻击;CSRF: 跨站请求伪造).
同源策略将限制以下行为:
- Cookie/LocalStorage/IndexDB
- DOM 无法获得
- Ajax 请求无法发送
跨域解决方案
1. JSONP
它的特性是简单/ 兼容性好/ 改造小, 但是不支持 POST 请求. 原理是通过 script
标签放入回调函数, 服务端将返回数据塞入回调函数即可.
1 | const createScript = url => { |
2. document.domain
通过设置域名来访问 Cookie
和 操作 iframe
窗口, 此方案只适用于主域相同, 子域不同的场景.
1 | // 父窗口 (http://www.main.com/a.html) |
3. window.location.hash
通过修改 hash 值来传递参数, 修改 hash 值并不会刷新页面但字节数有限制.
1 | // 父窗口向子窗口写入 hash |
4. window.name
window.name 只要在同一个窗口, 无论是否同源前一个页面设置了这个属性, 后一个页面就能读取. 它可以支持2M 大小的值但是变化需要自己监听.
1 | // 父窗口 (http://www.main.com/a.html) |
5. postMessage
postMessage(跨文档通信) 是 HTML5 中为了解决跨域出的 API, 可以解决以下几种问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
发送消息通过 postMessage(data, origin)
方法, 该方法接收两个参数:
- data: 需要传递的参数, 由于部分浏览器只支持字符串, 所以传递之前最好先
JSON.stringify()
序列化 - origin: 是接收方的 协议 + 主机 + 端口, 可以是设置为
*
, 指定与当前窗口同源的话设置为/
.
接收消息通过 message
事件监听, 该事件有一个 event
参数提供三个属性:
- event.origin: 消息接收方
- event.source: 消息来源
- event.data: 消息体
1 | // 父窗口 (http://www.main.com/a.html) |
5. CORS
跨域资源共享(Cross-origin-resource-sharing), 它允许浏览器向跨源服务器发出 XMLHttpRequest
请求. CORS 现在也是主流的跨域解决方案, 这种方式只需要后端做处理, 如果要带上 cookie 那么前后端都要设置.
带 cookie 传输:1
xhr.withCredentials = true
请求实例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const XMLRequest = (method = 'GET', url = '', data = null) => {
let xhr = null
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest()
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
xhr.open(method, url, true)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(data)
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
}
XMLRequest('POST', 'http://www.example.com/api', {
name: 'cara'
})
其中 readyState
有五种状态:
0: 为初始化
1: 服务器连接建立
2: 请求已接收
3: 请求处理中
4: 请求已完成
6. 代理服务器
其实就是通过配置 nginx 实现一个中间服务器作跳板, 代理到目标服务器.
静态资源代理1
2
3location / {
add_header Access-Control-Allow-Origin *;
}
具体代理配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16server {
listen 81;
server_name www.example.com;
lication / {
# 反向代理
proxy_pass http://www.example2.com:8080;
# 修改 cookie 域名
proxy_cookie_domain www.example2.com www.example.com;
index index.html index.htm;
# 不带 cookie 时才能设置为 *
add_header Access-Control-Allow-Origin http://www.example.com;
add_header Access-Control-Allow-Credentials true;
}
}
调用示例1
2
3
4
5let xhr = new XMLHttpRequest()
xhr.withCredentials = true
xhr.open('GET', 'htt://www.example.com:81/?user=admin', true)
xhr.send(null)
以上, 就是跨域导致的原因以及解决方案的大致总结.
Created on 2018-4-2 by Cara