理解跨域
目录
理解跨域
TLDR:
- 在响应头中加入CORS即可解决跨域问题。
request.setRequestHeader('Access-Control-Allow-Origin', 源);
- 不能使用CORS的场景下,JSONP方案也可,即前端引用不同源的js,后端返回一个包含了数据的js代码(可以是将数据写在window上或是按照前端要求使用约定的回调函数),实现跨域共享数据
//JSONP简化示意 //前端 const script = document.createElement('script') script.src = 跨域的某个js document.body.append(script);
//后端 window.xxx = 数据
1. 跨域与同源策略
同源策略就是禁止跨域访问,而跨域技术就是为突破同源策略而出现的。
1.1. 源与同源:
- 源=协议+域名+端口号,端口号默认80
- window.origin或location.origin获取当前源
- 同源:源相同,即协议、域名、端口号都相同
- 注意:要完全相同
- https://www.baidu.com/ 和 https://baidu.com/ 不同源
1.2. 同源策略及其目的
- 同源策略:浏览区故意设置的功能限制,不用源的页面之间,不准互相访问数据,主要有以下三种行为收到限制
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 无法获得
- AJAX 请求不能发送
- 同源策略(即禁止跨域访问)的目的:保护用户隐私
- 问题根源:黑客和正常的请求只有一个不同,就是请求的referrer(历史遗留问题,HTTP请求头中为referer,是错误拼写)
- 如果后端忘记检查源,那么用户数据就会被拿走,所以浏览器要主动防御这种行为
2. 跨域技术方案
2.1. CORS:Cross-origin resource sharing跨域资源共享
- 简单请求(不会触发CORS预检preflight的请求)
- 判定条件:需全部满足,这里只列出部分日常使用需注意的
- GET/HEAD/POST
- Content-Type 的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 其他要求详见MDN CORS部分
- 判定条件:需全部满足,这里只列出部分日常使用需注意的
//后端,添加响应头
response.setHeader( Access-Control -Allow-Origin', 源);
- 预检请求
- 需要预检的请求:需要使用OPTIONS方法发起预检请求到服务器以获知服务器是否允许该实际请求
//后端,添加响应头
response.setHeader('Access-Control-Allow-Origin', 'http://yangyixuan.icu'/*源,默认80端口*/)
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'/*允许的方法*/)
response.setHeader('Access-Control-Allow-Headers', 'X'/*自定义头部*/)
response.setHeader('Access-Control-Max-Age', 86400/* 86400=24*3600即一天 缓存时间*/)
- 附带身份凭证的CORS
- 当响应的是附带身份凭证的请求时,服务端必须明确 Access-Control-Allow-Origin 的值,而不能使用通配符“*”。
- CORS 预检请求不能包含凭据。预检请求的响应必须指定 Access-Control-Allow-Credentials: true 来表明可以携带凭据进行实际的请求。
//后端,添加响应头
res.setHeader('Access-Control-Allow-Origin',
'http://yangyiuxan.icu'/*不能使用'*'*/)
response.setHeader('Access-Control-Allow-Credentials', true);
2.2. JSONP
- JSONP是不支持cors情况下(IE),使用的另一种跨域方式,即前端引用不同源的js,后端返回一个包含了数据的js代码(可以是将数据写在window上或是按照前端要求使用约定的回调函数),实现跨域共享数据
- 思路:
- 前端与后端约定回调函数/变量名
- 前端引用JS文件(通过script标签)
- 后端按照约定将数据放入相应的JS中
- JS文件下载并执行回调后
- 优点
- 兼容IE
- 缺点
- script标签,读不到状态码,只知道成功或失败,没有AJAX精细
- 只能发GET,不支持POST
- 注意:
- 为防止函数名冲突,回调名字随机生成,以callback为键的参数,随机值为值,放在请求的查询参数中
- JSONP约定,query中的callback对应的值为JSONP回调函数
//JSONP Promise封装
//前端
function jsonp(url) {
return new Promise((resolve, reject) => {
const random = "frankJSONCALLbackName=" + Math.random()
window[random] = (data) => resolve(data)
const script = document.createElement("script")
script.src = `${url}?callback=${random}`
script.onload = () => {
script.remove()
}
script.onerror = () => {
reject()
}
document.body.appendChild(script)
})
jsonp("http://yangyixuan.icu/data.js")
.then(data => { console.log(data) })
//后端
// ....省略部分代码...
if (path === "/data.js") {
response.statusCode = 200;
response.setHeader("Content-Type", "text/javascript;charset=utf-8");
const string = `window['{{xxx}}']({{data}}) `
const data = fs.readFileSync("./public/friends.json").toString();
const string2 = string.replace("{{data}}", data).replace('{{xxx}}', query.callback);
response.write(string2);
response.end();
}
2.3. 其他跨域方式
- postMessage(ie不支持,主要用于网页间通信)
- websocket太重了
- cors中间件
- iframe过时
- 后端
- nodejs中间件
- nginx反向代理