跨域问题小结

引子

每次碰到跨域问题,总是要不停的 Googel 或者百度,然后找到一篇文章照着抄一抄,或者引用个大佬的配置,又或者用 webpack 配置配置就敷衍了事,这次这篇文章想探讨一下为什么会出现跨域问题,以及常见的跨域解决方案。

同源策略

为什么会出现跨域问题,这就不得不从浏览器的同源策略说起了:

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

那么同源是如何定义的呢?满足以下三个条件就成为同源:

  • 同一个协议,例如httphttps
  • 同一个域名,mocai.memoecai.com就不是同一个域名
  • 同一个端口,例如都是 80 端口

对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS 页面所在什么域

同源策略是浏览器策略

实现跨域的几种方式

JSONP

原理

jsonp是一种以前很常用的实现跨域的方式,它利用 HTML 中的script标签可以加载其他域的 js 的原理,将其他域的接口返回值直接作为 JS 代码执行。

例子

假设有这样一个接口

1
2
http://moecai.me/getWeather
// 接受一个参数cb 返回cb(res.data)

前端 JS

1
2
3
4
5
6
7
8
9
10
11
12
13
let btn = $(".btn");
btn.onclick = function () {
let script = document.createElement("script");
script.src = "http://moecai.me/getWeather?cb=appendChild";
document.head.appendChild(script);
document.head.removeChild(script);
};

function appendChild(data) {
let div = document.createElement("div");
div.innerText = data;
document.body.appendChild(div);
}

前端页面点击按钮就会发送一个请求获取一段字符串作为 js 执行,调用 appendChild 函数将 data 添加到页面上,这就是一个典型的jsonp的应用场景。

https://github.com/webmodules/jsonp

CORS

原理

cors 是 W3C 标准,IE10+的浏览器都实现了这个标准,使用 CORS 时,对前端来说只要正常的引用接口就行了,具体的跨域规则实际是在后端控制的。

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

例子

前端

1
2
3
4
5
6
7
8
9
10
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://moecai.me/getWeather");
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.log(xhr.status);
}
};
xhr.send();

后端

1
2
3
4
5
6
7
8
9
10
11
12
const http = require("http");
const url = require("url");
let data = {
Beijin: "Rain",
};
const server = http.createServer(function (req, res) {
let pathObj = url.parse(req.url, true);
if (pathObj.pathName === "/getWeather") {
res.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
res.end(JSON.stringify(data));
}
});

后端在返回的响应头设置Access-Control-Allow-Origin属性即可,上面的例子,后端就允许了http://localhost:8080源的请求。

代理

代理,将前端请求转发到后端域名上,直接避免跨域,一般在前端开发时会通过配置 webpack 使用代理的方式来避免跨域问题,当然在生产上可以通过使用 nginx 配置代理来实现跨域:

1
2
3
4
5
6
7
8
9
10
server{
# 监听9099端口
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
location ^~ /api {
proxy_pass http://localhost:9871;
}
}

小结

常见的跨域方式就是以上三种了,实际上现在工作中已经都是通过 CORS 和代理的方式来实现跨域了,不过在某些特殊场景下还是会使用 JSONP。

作者

bert_cai

发布于

2019-07-04

更新于

2020-11-16

许可协议

评论