关于跨域

跨域问题嘛,总会遇到的,嘿嘿

虽然说浏览器的同源策略有效的隔离恶意文件,但是耐不住开发环境、测试环境、生产环境的地址存在不一样的可能性嘛

参考链接

跨域
不要再问我跨域的问题了
九种跨域方式实现原理(完整版)

跨域问题的原因

其实就是浏览器的同源策略导致了跨域的问题。

简单来说,同源其实就是"协议、域名、端口"相同才同源,即使指向统一 ip 地址的不同域名也是不同源的。
同源策略有效的防止了XSSCSRF等攻击。

Allow-Control-Allow-Origin: *

下载地址

chrome 的一个插件。

遇到过不好用的时候

Allows to you request any site with ajax from any source. Adds to response ‘Allow-Control-Allow-Origin: *‘ header

原理应该和 CORS 很像,就如介绍所言在 header 里添加了 Allow-Control-Allow-Origin: *

JSONP

JSONP 是什么

<link href=''/>
<img src=''/>
<script src=''/>

↑ 这三个标签是允许跨域加载资源的。

而 JSONP 就是前端定义一个回调函数,使用 script 标签向某个不同源的地址请求数据,然后后台返回前端定义的这个函数,最后会运行这个函数,而函数的参数里带着请求的数据。

JSONP 封装
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
// 定义
const jsonp = ({ url, params, callback }) => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
params = { ...params, callback };
let arr = [];
for (key in params) {
arr.push(`${key}=${params[key]}`);
}
window.callback = data => {
document.body.removeChild(script);
delete window.callback;
resolve(data);
};
script.src = `${url}?${arr.join("$")}`;
document.body.appendChild(script);
});
};
// 调用
jsonp({
url: "xxx",
params: { msg: "message" },
callback: "show"
}).then(data => {
console.log(data);
});

两个小缺点

  1. 仅支持 GET 方法
  2. 可能会遭受到 XSS 攻击

jsonp 与 ajax 的区别

虽然都是从客户端向服务端发送请求,但是 ajax 的核心是 XMLHttpRequet ,而 jsonp 的核心是 script 标签允许跨域请求,有点钻空子的感觉。

CORS

跨域资源共享 CORS 详解

全称 Cross-origin resource sharing (跨域源资源共享),定义了在请求跨源资源时客户端和服务端如何沟通。

大概的思路就是使用自定的 HTTP header 让浏览器和服务器进行沟通,从而决定请求的成败。

CORS 请求被浏览器分为简单请求和复杂请求 。

简单请求

条件是使用的方法为 GET / POST / HEAD
且 header 的信息仅能有

  1. Accept
  2. Accept-Language
  3. Content-Language
  4. Last-Event-ID
  5. Content-Type:值只限于 application/x-www-form-urlencoded、multipart/form-data、text/plain

大概的流程:

  1. 简单的 GET / POST 请求
  2. 发送时浏览器会自动附加一个额外的 origin header ,包含请求页面的源信息,就是协议、域名和端口
  3. 服务端根绝这个 origin header 判断是否响应,
  4. 若可以接受,则在返回的请求里的 Access-Control-Allow-Origin 里添加同源信息(公共信息则可以返回"*")
  5. 如果不存在这个 Access-Control-Allow-Origin 或者存在但是内容不匹配,则浏览器会驳回请求。

所以前端正常的发送请求就 ok 了

复杂请求

条件是使用的方法为 PUT / DELETE 或者有多余的 header 或 header 的 Content-Type 值为 application/json

复杂请求会在正式的通信前额外增加一个 HTTP 请求,称为 Preflighted Requests

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错。 – 跨域资源共享 CORS 详解

还是需要后端进行更多的支持。

如果需要在 HTTP 请求中携带 cookie ,则需要设置如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用 ajax 时
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// 或者使用 fetch() 时
fetch(`http://somewhere-else/cors?msg=helloCors`, {
// 需要带上cookie
credentials: "include",
// 这里添加额外的headers来触发非简单请求
headers: {
msg: "extra headers"
}
}).then(res => {
console.log(res);
});

Web Sockets

h5 的新协议,基于 TCP ,与 HTTP 兼容但是并不融入 HTTP ,目标是在一个单独的持久连接上提供双工、双向通信,建立成功后,服务端和客户端都能主动向对方接受/发送信息。

也就是所 ws 连接建立,除非某一方断开接连,服务端和客户端就一直保持通话状态。

但是建立连接还是要以来 HTTP 的,建立好之后就没 HTTP 什么事情了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 需要填入绝对地址,同源策略对于 Web Sockets 并不适用
let socket = new WebSocket("https://somewhere-else.com/cors");
// Web Sockets 只支持发送纯文本数据,在传数据前需要先进行序列化。
socket.send(`hello world!`);
// 服务器向客户端发送消息时会出发 message 事件。
socket.message = data => {
// data 也是字符串
console.log(data);
};

// 以下三个事件分别在连接建立成功 / 发生错误,连接无法持续 / 关闭连接时触发
socket.open();
socket.error();
socket.close();

domain

之前在实习的时候使用了一次,而且还失败了(可恶
但是当时 ddl 快到了所以直接临时在后端换了一个中间件来解决跨域问题,没有深入了解 domain 解决跨域问题的原理。

使用 domain 来解决跨域的问题有(相比之下)较大的局限性,只能在二级域名相同的情况下使用,并且需要配合 frame 标签使用。

原理又是两个页面都通过 js 设置 document.domain 为基础主域,实现同域。

再两个二级域名相同的网页,比如 a.demo.com:3000/a.html 以及 b.demo.com:3000/b.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// a.html
<body>
<iframe
src="http://b.demo.com:3000/b.html"
frameborder="0"
onload="load()"
id="frame"
></iframe>
<script>
// 在两个页面都要设置 document.domain
let frame = document.getElementById("frame");
document.domain = "demo.com";
function load() {
console.log(frame.contentWindow.a);
}
</script>
</body>
// b.html
<body>
<script>
document.domain = "demo.com";
var a = 100;
</script>
</body>
0%