同源策略及跨域问题
同源策略是浏览器的一套安全机制,当一个源的文档和脚本,与另一个源的资源进行通信时,同源策略就会对这个通信进行不同程度的限制。
对同源进行放行,对异源进行限制。
同源和异源
源(origin)= 协议 + 域名 + 端口
跨域出现的场景
- 网络通信:a元素跳转,加载css,js,图片,ajax请求等。
- JSAPI:window.open(),window.parent()等。
- 存储:webStorage,indexDB等。
网络中的跨域
当浏览器加载页面后,会发出很多请求,比如:css,js,图片,ajax等。
发出请求页面称为页面源,请求目标称为目标源。当页面源和请求源一致时,称为同源,否则为异源。
浏览器如何限制异源请求
当js发出一个ajax请求之后,请求其实是发送到了服务器,浏览器会对请求进行校验。
解决方案
CORS
CORS(Cross-Origin Resource Sharing)是最正统的跨域解决方案。
CORS的基本理念:
- 只要服务器明确表示
允许,则校验通过。 - 服务器明确表示拒绝或没有表示,则表示不通过。
请求分类
CORS 将请求分为两类:简单请求和预检请求。
简单请求: 只要全部满足下面条件,就是简单请求
- 请求方法是 GET,POST,HEAD之一。
- 头部字段满足CORS安全规范(详见W3C):浏览器默认自带的头部字段都是满足安全规范的,只要开发者不改动和新增头部,就不会打破次规则。
- 如果有 Content-Type,必须是下列值中的一个。
- text/plain
- mutltipart/form-data
- application/x-wwww-form-urlenccoded
预检请求: 只要不是简单请求,就全部是预检请求
简单请求的校验规则
会将请求发送出去,然后在头部带上一个Origin:当前页面源的字段,让服务器去判定是否可以,服务器会在响应头里面带上一个 Access-Control-Allow-Origin:源字段。或者直接将值写为*。
预检请求的校验规则
- 发送预检请求
不会发送真实的请求,先发送一个预检请求,如下图:
服务器自行去判断是否响应请求,如下图:
- 发送真实请求
当预检请求通过以后,浏览器会发送一个跟普通请求一样的真实请求。
细节1:默认情况下,跨域请求都不会携带cookie,这样一来如果需要去做一些权限的话就会出问题。
不过可以根据配置:服务器需要带一个响应头去允许携带凭证,如果带了cookie,服务器 Access-control Allow Origin就不能设置为 *
细节2:关于跨域获取响应头
JSONP
只能发送get请求。
容易产生安全隐患:
恶意攻击者可能利用callback=恶意函数的方式实现xss攻击
代理
// proxy
app.get('/hero', async (req, res) => {
const axios = require('axios');
const resp = await axios.get('https://pvp.qq.com/web201605/js/herolist.json');
// 使用CORS解决对代理服务器的跨域
res.header('access-control-allow-origin', '*');
res.send(resp.data);
});