02.浏览器的同源策略
大约 3 分钟
简版
- 浏览器处于安全考虑,对同源请求放行,对异源请求限制。这些限制规则统称为同源策略。
- 因这些限制问题造成的开发问题,称之为跨域(异源)问题。
同源
同源 = 协议 + 域名 + 端口号
同源请求
同源请求 = 页面源和目标源相一致的情况下
同源策略
- 浏览器如何限制异源请求?
- 对标签发出的跨域请求轻微限制 link script img video audio
- 对AJAX发出的跨域请求严厉限制
对AJAX的限制
- 用户用XHR或者fetch对浏览器发出跨域请求,浏览器对服务器发出请求
- 服务器响应浏览器的请求,浏览器同时进行校验,通过则交付,不通过则限制(引发错误,跨域问题)
跨域问题的解决方案
CORS(Cross-Origin Resource Sharing)
在浏览器对服务器的响应校验时,校验有校验规则CORS
规则
- CORS是一套机制,用于浏览器校验跨域请求
- 它的基本理念是
- 服务器只要明确表示允许,则校验通过
- 服务器明确表示拒绝或没有表示,则校验不通过
- 使用CORS,必须确保服务器是自己的服务器,别人的服务器是不行的
CORS把请求分为俩类
- 简单请求
- 请求的主要方式有:GET HEAD POST
- 头部字段满足CORS安全规范,详见[W3C](Fetch Standard (whatwg.org))
- 改变头部就是不满足CORS安全规范
- 请求头的Content-Type为
- text/plain
- multipart/form-data
- application/x-www-form-urlcoded
- 预检请求
- 非简单请求
- 简单请求
简单请求:服务器对浏览器响应是添加一个响应头
Access-Control-Allow-Origin:* //或者是浏览器向服务器发出请求时,请求头里的Origin里的源预检请求
- 浏览器向服务器发送预检请求
Origin:源 Access-Control-Resquest-Method:POST Access-Control-Resquest-Headers:a,b,content-type- 服务器对浏览器发出响应
Access-Control-Allow-Origin:* //或者是浏览器向服务器发出请求时,请求头里的Origin里的源 Access-Control-Allow-Method:POST Access-Control-Allow-Headers:a,b,content-type Access-Control-Max-Age:86400
JSONP(JSON with Padding)
JSONP是解决跨域问题的古老方案。同源策略中对标签的跨域请求限制较小,JSONP就是利用了这一点
JSONP只有get请求
方案
- 创建script元素并发送跨域请求
<script> /** * 准备一个回调函数 * 服务器响应后会运行这个函数 * 并传递响应数据给参数 * @param jsonp */ function callback(jsonp) { console.log(jsonp) } function request(url){ const script = document.createElement('script') script.src = url script.onload = function () { this.remove() } document.body.appendChild(script) } document.querySelector('button').onclick = function () { request('http://localhost:3000/say?wd=hello&callback=callback') } </script>- 服务器响应结果是一个函数调用
callback([1,2,3])
注意
CORS和JSONP均对服务器有要求
代理
- 方案
- Proxy是自己的服务器
- 请求:浏览器跨域请求Proxy代理服务器,Proxy代理服务器转发请求到target目标服务器
- 响应:target目标服务器对Proxy服务器发出响应,Proxy代理服务器通过CORS或者是JSONP对浏览器发出响应
<script>
/**
* 代理服务器
*/
const express = require('express')
const app = express()
//接受对路劲为/api的请求
app.get('/api', async (req, res) => {
//使用CORS解决对代理服务器的跨域问题
const axios = require('axios')
const resp = await axios.get('https://pvp.qq.com/web201605/js/herolist.json')
res.header('Access-Control-Allow-Origin', '*')
//返回数据
res.send(resp.data)
})
//监听3000端口
app.listen(3000, () => {
console.log('server is running at port 3000')
})
</script>
使用场景
- 能改变服务器吗?
- 能
- 浏览器支持CORS吗?
- 支持 CORS
- 不支持 JSONP
- 浏览器支持CORS吗?
- 不能
- 代理
- 能