飞雪连天射白鹿,笑书神侠倚碧鸳

0%

轻松处理10种跨域请求

Leo:跨域啦,跨域啦,求后端小哥开放白名单吧
大佬:自己解决
Leo:/(ㄒoㄒ)/~~

记得以前最频繁的处理跨域需求就是本地调不通测试站点的接口,只好找后端重新部署一次,开放CORS限制

后来有了nginx和cli-proxy就方便多了


什么是跨域啦

  • 浏览器侧的同源策略,用来限制请求资源交互,减少服务器被攻击的渠道之一
  • 服务端侧是没有的,所以有些新入门/不了解的服务端同学会疑惑postman上可以正常请求

什么是同源啦

protocol(协议)、domain(域名)、port(端口)三者一致

http即默认80
https即默认443

说到domain域名,示例map.baidu.com

  • 维基百科

    • baidu.com叫做二级域名
    • map.baidu.com叫做三级域名
  • 而我更倾向于称呼

    • baidu.com叫做一级域名,【主域名】
      • .com是顶级域,baidu是一级名
    • map.baidu.com叫做二级域名,【子域名】
      • map是二级名
    • 不接受反驳,你上阿里云/腾讯云买的是后缀域和名,可以自行配置多个子域名

解决它

1、CORS控制响应头

Access-Control-Allow-Origin控制哪些域名可以获取资源

通常解决是设置响应头Access-Control-Allow-Origin: *

简单/复杂请求

触发简单:

  • Method
    • GET、HEAD、POST
  • Content-Type
    • text/plain、multipart/form-data、application/x-www-form-urlencoded
  • header
    • Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width
  • XMLHttpRequestUpload没有事件监听
  • ReadableStream不存在

触发预检:Options

就是除了简单条件以外的,比如put和application/json

ie9请使用jsonp

Origin请求头

  • 存在跨域,浏览器带上Origin
  • 不存在跨域,请求不带Origin
1
2
3
4
5
6
async xxx(ctx,next){
const getOrigin = ctx.get('Origin');
if(!getOrigin) return await next();
// 解决跨域请求
ctx.set('Access-Control-Allow-Origin',getOrigin)
}

Vary缓存处理

当存在多个域名需要通过CORS时,会存在响应缓存,需要【避免CDN缓存】,可以配置Vary:Origin

1
2
3
const getOrigin = ctx.get('Origin');
ctx.set('Vary', 'Origin')
// xxx

cookie处理

  • withCredentials为true
  • Access-Control-Allow-Origin为非 *
  • Access-Control-Allow-Credentials 为 true

2、正向代理Node proxy

代理的是客户端

1
2
3
4
5
6
// 本地开发时的接口请求域名设为空
proxy: {
"/api": {
target: "http://10.130.10.10:1234"
}
}

charles抓包工具拦截处理代理

在 tools/map remote 中设置Map from和Map to

3、反向代理Nginx

代理的是服务端

1
2
3
location /api {
proxy_pass http://localhost:8080;
}

4、JSONP

script标签,GET方法

  • 定义函数jsonpCallback=function(){}
  • 定义请求参数cb=jsonpCallback
  • 动态接口返回结果运行jsonpCallback({xxx:xxx})

5、Websocket

客户端和服务器之间存在非HTTP,持久,全双工连接

因为不用HTTP,所以就没限制了

6、window.postMessage

可以在 http 返回头 添加X-Frame-Options: SAMEORIGIN 防止被别人添加至 iframe

<iframe id="frame" frameborder="0" onload="load()" src="http://b.test.com/b.html"></iframe>

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的 iframe 消息传递

7、document.domain + iframe

相同主域名,页面A可以拿到iframe页面B的内容

8、window.location.hash + iframe

A:<iframe src="http://localhost:8080/hash/c.html#name1"></iframe>

C:const iframe = document.createElement("iframe"); iframe.src = "http://localhost:8000/hash/b.html#name2"; document.body.appendChild(iframe);

B:window.parent.parent.location.hash = location.hash;

页面A的url的hash值 -> C AC跨域
页面C的url接收hash值 -> B BC跨域
页面B结果给页面A的hash值 -> A AB跨域

9、window.name + iframe

location 变化,重新加载,name 可以不变

就是先读取一次后换个iframe的src路径重新加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// A:
let first = true;
// onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
function load() {
if (first) {
// 第1次onload(跨域页)成功后,切换到同域代理页面
iframe.src = "http://localhost:8000/name/b.html";
first = false;
} else {
// 第2次onload(同域b.html页)成功后,读取同域window.name中数据
console.log(iframe.contentWindow.name);
}
}
// b.html
<div></div>
// c.html
<script>
window.name = "秋风的笔记";
</script>

10、浏览器配置

【慎用】

Windows
找到你安装的目录
.\Google\Chrome\Application\chrome.exe –disable-web-security –user-data-dir=xxxx

Mac
/Downloads/chrome-data
/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary –disable-web-security –user-data-dir=
/Downloads/chrome-data

听说,打赏我的人最后都找到了真爱
↘ 此处应有打赏 ↙
// 用户脚本