跨域

为什么跨域?

目前市场 IT 项目的开发模式:

  • 集成开发
    • 请求本公司的其他服务器
    • 请求第三方服务器资源
    • jsonp (老项目)
  • 前后端分离
    • 请求本公司的其他服务器
    • 请求第三方服务器资源
    • jsonp (老项目)

由于同源策略策略的原因,浏览器为了保护服务器出台了同源策略

同源策略 (same origin policy)

协议、域名、端口号都相同被视为同源

  • 协议 (tcp/ip)
    • 文件协议
      • file
    • 网络协议
      • http
      • https
    • 传输协议
      • SMTP
  • 域名
  • 端口号

常见协议的分类

  • FTP 传输文件
  • SMTP 收发邮件
  • TCP 网络协议
  • UDP 应用传输协议

抓包工具

怎么跨域?

* PROXY (vue、react)

vue-vue.comfig.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module.exports = {
devServer: {
proxy: {
"/api1": {
// 跨域的目标地址
target: "http://localhost:7001",
changeOrigin: true,
// 路径重写
pathRewrite: {
"^/api1": "",
},
},
"/api2": {
// 跨域的目标地址
target: "http://localhost:7002",
changeOrigin: true,
// 路径重写
pathRewrite: {
"^/api2": "",
},
},
},
},
};

JSONP

由于 html 标签中,带有 src 的属性,不受同源策略的约束,我们就可以通过,动态创建 script 标签来实现跨域

区分 JSONP 和 XHR

  • 看请求类型
  • 看参数有没有 callback

主要利用了回调函数,步骤如下:

  • 前端向服务端传递一个回调函数
  • 服务端调用函数,传入参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get("/api", (req, res) => {
// 解析jsonp参数
const oUrl = url.parse(req.url);
const oQuery = queryString.parse(oUrl.query);
const data = {
code: 1,
type: "json",
};
if (!oQuery["callback"]) {
res.end(JSON.stringify(data));
} else {
res.end(`${oQuery.callback}(${JSON.stringify(data)})`);
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function jsonp(url, opt = {}, callback) {
const defaultOpt = {
jsonpCallback: "__jp",
prefix: "callback",
};
// 扩展运算符
// opt = { ...defaultOpt, ...opt };
// es6 对象的ip
opt = Object.assign(defaultOpt, opt);
const oScript = document.createElement("script");
const oWrap = document.body || document.documentElement.body;
if (!url.includes(opt.prefix)) {
url += `?${opt.prefix}=${opt.jsonpCallback}`;
}
oScript.src = url;
oScript.onload = function () {
console.log("onload");
callback(null, "ok");
};
oScript.onerror = function () {
console.log("onerror");
callback("guale", null);
};
window[opt.jsonpCallback] = function (data) {
console.log(data);
};
oWrap.appendChild(oScript);
}

const url = "http://localhost:3000/api";
jsonp(url, {}, (err, res) => {
console.log(err, res);
});

URL 编码:

  • encodeURI

  • decodeURI

  • encodeURIComponent

  • decodeURIComponent

    函数 描述 FF N IE
    decodeURI() 解码某个编码的 URI。 1 4 5.5
    decodeURIComponent() 解码一个编码的 URI 组件。 1 4 5.5
    encodeURI() 把字符串编码为 URI。 1 4 5.5
    encodeURIComponent() 把字符串编码为 URI 组件。 1 4 5.5
  • window.onload

  • DomContentLoaded

  • query.ready | $.ready

数据类型

  • json
  • xml (了解)

CORS

cors 被称为服务端跨域,说白了就是服务器允许某个地址访问
下面通过 nodejs 小型 web 服务器 express 举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 原生nodejs
app.get("/api", (req, res) => {
res.writeHead(200, {
"Access-Control-Allow-Origin": "*",
});
res.end(
JSON.stringify({
code: 1,
msg: "123",
})
);
});

// express的写法
app.get("/api", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.json({
code: 1,
msg: 123,
});
});

服务器代理

  • 正向代理

由于服务器请求服务器的数据,不受同源策略的影响,我们可以通过 http.request 来请求目标服务器数据,然后响应给前端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
app.get("/getSingerList", async (req, res) => {
const url = "https://u.y.qq.com/cgi-bin/musics.fcg";
const result = await axios.get(url, {
headers: {
origin: "https://y.qq.com",
referer: "https://y.qq.com",
},
params: {
"-": "getUCGI8660003538803218",
g_tk: 5381,
sign: "zzaqjmvsi3fu8i2f3fef04bda3a286eeb40136f6b80f4a",
loginUin: 0,
hostUin: 0,
format: "json",
inCharset: "utf8",
outCharset: "utf-8",
notice: 0,
platform: "yqq.json",
needNewCode: 0,
data: {
comm: { ct: 24, cv: 0 },
singerList: {
module: "Music.SingerListServer",
method: "get_singer_list",
param: {
area: -100,
sex: -100,
genre: -100,
index: -100,
sin: 0,
cur_page: 1,
},
},
},
},
});
res.json({ code: 1, result: result.data.singerList.data.singerlist });
});
  • 反向代理

伪造请求

AJAX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function ajax(url, callback) {
// 兼容问题
// 高级浏览器支持XMLHttpRequest
// 低版本IE通过
let xhr;
if (window.ActiveXObject) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
xhr.open("GET", url, false);
xhr.onreadystatechange = function () {
if (!xhr.readyState == 4) return;
if (xhr.status == 200) {
callback(xhr.responseText);
}
};
xhr.send();
}
ajax("http://localhost:3000/api", (data) => {
console.log(JSON.parse());
});