跨域

什么是跨域?

跨域的产生是由于浏览器的同源策略,且WEB服务和资源处于不同的服务器上,这个时候,当浏览器向资源服务器发起请求时,由于不同源的原因,就会出现跨域问题。

但像图片、CSS、脚本等基础资源是可跨域加载的。

浏览器对于资源的请求流程图如下:

跨域的解决方案

目前常见的跨域解决方案有:

  • Web服务器和资源服务器合并在同一台服务器(或同一个域名)下运行,其实就是同源部署,没必要多说了。

  • node代理服务器。

  • CORS。

  • Nginx反向代理。

在以前流行过JSONP解决跨域的方案,其实就是利用了脚本资源是可跨域加载的这一特性。

浏览器后来也推出了postMessage API,感兴趣的可以看一下。

还有将所有请求都使用websocket协议的,其实就是利用了WebSocket协议不受同源策略限制这一特性。

还有什么利用允许跨域加载的HTML标签(link标签引入,在style标签中加载字体文件等等),还有安装CORS解除插件啥的,都基本很少用,这里就没必要赘述了。

CORS

CORS(跨域资源共享)是一种基于HTTP头的机制,它允许服务器指示除其自身以外的任何源(域、协议或端口),浏览器应允许从这些源加载资源。CORS机制让Web应用服务器能控制跨域访问,从而实现安全的跨域数据传输。

既然CORS是基于HTTP头的机制,它与下面这些HTTP头有关:

  • 请求头:由浏览器自己添加。

    • Origin
    • Access-Control-Request-Method(预检请求中用到)。
    • Access-Control-Request-Headers(预检请求中用到)。
  • 响应头:开发者自己添加。

    • Access-Control-Allow-Origin:声明允许的源。
    • Access-Control-Allow-Methods:允许的 HTTP 方法。
    • Access-Control-Allow-Headers:允许的自定义头。
    • Access-Control-Allow-Credentials:是否允许发送凭证(cookies)。
    • Access-Control-Max-Age:预检请求的缓存时间。

浏览器对于CORS请求分为简单请求和预检请求。

简单请求:

  • 请求方法为 GET、POST、HEAD。

  • 无自定义头。

  • Content-Typetext/plain, multipart/form-data, application/x-www-form-urlencoded

不满足简单请求条件的就是预检请求。

前面我们说了,我们只需要在服务端进行对应配置即可,这里以在node中使用express为例,给出解决代码:

推荐直接使用cors库:

1
npm i cors

express配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const express = require('express');
const cores = require('cors');

const app = express();

app.use(
cores({
// 允许的域名
origin: 'http://localhost:8080',
// 允许的请求方法
methods: ['GET', 'POST', 'DELETE', 'PUT'],
// 允许的请求头
allowedHeaders: ['Content-Type', 'Authorization'],
// 允许携带cookie
credentials: true, // Access-Control-Allow-Origin 不能为 *
})
);

app.get('/serve', (req, res) => {
res.send('Hello test!');
});

app.listen(3000, () => console.log('Server is listening on port 3000'));

按照自己的需要在cores的配置中填写即可,无论什么语言,只要后端服务能配置上述相同效果即能解决跨域。

Nginx反向代理

nginx.conf中加入如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}

# 匹配/api/开头的请求,转发到proxy_pass指定的地址
location /api/ {
# 这个是必备的,是我们转发的api接口
proxy_pass http://127.0.0.1:8001/;

# 下面这些就是设置与跨域有关的HTTP头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers "Accept,Accept-Encoding,Accept-Language,Connection,Content-Length,Content-Type,Host,Origin,Referer,User-Agent";
add_header Access-Control-Allow-Methods 'GET,POST,PUT,OPTIONS';
add_header Access-Control-Allow-Credentials true;

if ($request_method = 'OPTIONS') {
# HTTP 204 No Content 成功状态响应码,表示该请求已经成功了,但是客户端客户不需要离开当前页面。默认情况下 204 响应是可缓存的。
# 该响应中不得包含任何内容或 Content-Length 标头。
# 其实就是只返回一个成功的状态,用204来表示.
return 204;
}
}