# Nginx教程 - 10 前后端分离与跨域
# 10.1 概念解释
# 10.1.1 什么是前后端分离
前后端分离 是一种软件架构设计模式,它将传统的Web应用分为前端和后端。
前端(Frontend):负责用户界面和用户交互部分,通常使用HTML、CSS和JavaScript开发。现代前端框架包括React、Angular和Vue.js等。
后端(Backend):负责业务逻辑、数据处理和数据库交互,通常使用服务器端编程语言如Node.js、Python、Java、PHP等开发,并提供API(如RESTful或GraphQL)供前端调用。
前后端分离的好处:
提升开发效率:前后端分离允许前后端开发团队并行工作,互不影响。前端团队可以专注于页面的交互逻辑和用户体验,后端团队则专注于业务逻辑和数据处理。
降低维护成本:由于前后端代码是分离的,因此当其中一方需要修改时,不会影响到另一方。这大大降低了代码的耦合性,提高了系统的可维护性。
提高系统性能:前端通过异步方式与后端进行数据交互,可以实现页面的局部刷新,减少不必要的流量传输,提高系统的响应速度和用户体验。
支持跨平台:前后端分离使得前端可以独立于后端进行部署和测试,从而支持更多的平台和设备。
# 10.1.2 什么是跨域
跨域(Cross-Origin)是指浏览器的同源策略(Same-Origin Policy),同源策略是浏览器的一种安全策略,浏览器会限制了从一个域(域名、协议和端口相同)加载的文档或脚本去访问另一个不同域的资源。这种限制是为了防止一个网站恶意读取另一个网站的敏感信息。
以下 URL 都被认为是不同的源:
http://doubibiji.com
与http://www.doubibiji.com
(子域名不同)http://www.doubibiji.com:80
与http://www.doubibiji.com:8080
(端口不同)http://www.doubibiji.com
与https://www.doubibiji.com
(协议不同,http和https)
在前后端分离的项目中,前端和后台服务不是在同一个项目中,这会导致前后端的IP地址、端口或域名不一致,从而产生跨域问题。具体来说,当前端页面尝试通过Ajax等技术向后端接口请求数据时,如果前后端不满足同源策略的要求,浏览器就会阻止这次请求,从而引发跨域问题。
例如,当Spring Boot后端服务运行在一个端口上,而Vue前端服务运行在另一个端口上时,由于两者端口不同,因此不满足同源策略的要求。此时,当Vue前端尝试通过Ajax请求Spring Boot后端接口时,就会遇到跨域问题。
# 10.1.3 跨域问题的解决方法
# 1 JSONP
JSONP(JSON with Padding)是一种通过 <script>
标签加载 JSON 数据的方式,但它只支持 GET 请求。JavaScript跨域请求数据是不可以的,但是使用 <script>
标签跨域请求其他站点的JavaScript脚本是可以的。可以把数据封装到JavaScript脚本中,作为脚本中函数的参数,然后将脚本返回给客户端,客户端获取脚本后,立即执行脚本,就可以获取到函数的参数,也就是服务器传递的数据了,从而解决了跨域问题。
JSONP是一种比较老的方式,目前已经被 CORS 大部分取代。
# 2 CORS
这是一种在服务器端通过设置适当的 CORS 头来允许跨域请求。也就是服务器告诉浏览器,我允许被跨域来访问,不要拦截了。
例如可以通过在Spring Boot项目中添加CORS配置类来实现,允许前端页面的域名访问后端接口。
# 3 使用反向代理
通过配置反向代理,前端所有的请求都会发送到同一个域名,也就是 Nginx 接收所有的请求,包括前端静态页面和后端动态数据接口的请求,然后 Nginx 反向代理服务器再根据请求路径将后端请求转发到对应的后端服务器,这就避免了浏览器的跨域限制。
# 10.2 反向代理配置
使用 Nginx 进行前后端分离的配置,使用的是反向代理配置来实现的,所以先介绍一下 Nginx 中的反向代理配置。
使用反向代理,请求发送给 Nginx,Nginx 将请求转发到其他的服务器。
反向代理使用 proxy_pass
来配置。举个栗子:
server {
listen 80;
server_name localhost;
location / {
proxy_pass https://www.baidu.com;
}
}
2
3
4
5
6
7
8
上面的配置,当访问 Nginx 的时候,将请求转发到 http://www.baidu.com
。
而且页面搜索的时候,地址栏仍然显示的 Nginx 的地址。
# 10.3 proxy_pass配置注意点
proxy_pass
的语法是:
proxy_pass url;
这里的 url
分为带 /
和不带 /
两种情况,这两种情况处理有一些不同。
带 /
为绝对根路径,location
后的内容不会拼接到转发地址;不带 /
表示相对路径,会将 location
后面的内容也拼接到转发路径上。
举个栗子:
当配置如下:
location /apis/ {
proxy_pass http://192.168.0.102:8080/ # 后面带/
}
2
3
访问路径为:http://localhost/apis/index.html
,转发后的请求为:http://192.168.0.102:8080/index.html
;
当配置如下:
location /apis/ {
proxy_pass http://192.168.0.102:8080 # 后面不带/
}
2
3
访问路径为:http://localhost/apis/index.html
,转发后的请求为:http://192.168.0.102:8080/apis/index.html
;
# 10.4 前后端分离配置
在前后端分离的项目,可以让所有的请求都请求到 Nginx,然后 Nginx 拦截所有的请求。
但是如果是 /apis/
开头的请求,就转发到后端的 Tomcat,这样的话,就实现了前后端请求的分发,而且实现了跨域,因为前后端请求的协议、域名、端口都是一样的。
在 Nginx 中配置如下:
server {
listen 80;
server_name localhost;
# 如果是/apis/开头的请求,就转发给后端服务器
location /apis/ {
proxy_pass http://192.168.0.102:8080/; # 后端tomcat地址
proxy_set_header Host $proxy_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#上传文件大小配置
client_max_body_size 100M;
}
# 其他请求就交由nginx处理
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
当访问 http://192.168.0.108/apis/hello
之后,/apis/
之后的内容会拼接在 http://192.168.0.102:8080/
之后,变成 http://192.168.0.102/hello
(不包括 /apis/
),去请求后端的 tomcat 服务器。
# 10.5 Nginx跨域配置
如果有两台 Nginx 服务器,Nginx1 服务器的站点想要访问 Nginx2 服务器站点的资源,也可能会遇到跨域问题。
可以在 Nginx2 站点进行配置,支持跨域访问,则需要在 Nginx 中进行配置:
server {
listen 80;
server_name www.doubibiji.com;
location / {
# 允许 所有头部 所有域 所有方法
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Methods' '*';
# 允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' "true";
# 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Methods' '*';
add_header 'Access-Control-Allow-Credentials' "true";
return 204;
}
# ...其他配置
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
预检请求是使用 OPTIONS
方法发送的。这种请求的目的是在实际请求之前与服务器进行通信,检查服务器是否允许具体的跨域请求。
也可以限制指定的域、方法来跨域访问:
server {
listen 80;
server_name www.doubibiji.com;
map $http_origin $cors_origin {
default '';
"https://www.abc.com" $http_origin;
"https://www.bcd.com" $http_origin;
"https://www.cde.com" $http_origin;
}
location / {
if ($cors_origin) {
# 允许多个域跨域来访问
add_header 'Access-Control-Allow-Origin' $cors_origin;
# 允许指定的一个域跨域访问
# add_header 'Access-Control-Allow-Origin' 'http://www.abc.com';
# 允许的方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
}
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
return 204;
}
# ...其他配置
}
}
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