最近在准备一个小项目来着,使用tomcat部署后端的SpringBoot项目,然后使用Nginx部署前端界面并转发后端请求到相应的tomcat集群时,发生了跨域问题,解决过程欲仙欲死,特在此记录一下。
- 环境介绍
- 本地物理主机,配置host
192.168.182.135 api.mk-shop.com 192.168.182.135 shop.mk-shop.com 192.168.182.135 center.mk-shop.com
- 四台虚拟机,IP如下
# tomcat主机1 192.168.182.130 # tomcat主机2 192.168.182.131 # nginx主机1 192.168.182.135 # nginx主机2 192.168.182.132
- 开放端口
80/tcp 8080/tcp 8088/tcp 3306/tcp
- 项目部署
- 前端项目部署在nginx本地,作为静态资源文件部署
- 后端项目部署在tomcat中,作为tomcat集群形式存在
请求过程
- 实体主机作为请求发起端(客户端),访问shop.mk-shop.com(也就是Nginx服务器所在的主机地址)获取到前端页面的index.html文件,浏览器解析页面中的异步请求后在客户端浏览器向Nginx主机再次发起请求,获取到动态加载的数据。
- 由于配置了nginx作为反向代理服务器,对于客户端而言请求都是发送给nginx代理服务器的,因此Nginx主机监听的都是客户端请求过来的url地址或者说是域名。
跨域处理
Nginx配置,一下配置只使用了一台Nginx(192.168.182.135)和两台Tomcat虚拟机。
# work的执行者,涉及到权限问题
user root;
# work进程的个数
worker_processes 3;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
# 默认的事件调度机制 [ kqueue | rtsig | epoll | /dev/poll | select | poll ]
use epoll;
# 每个work能连接的最大请求格式
worker_connections 1024;
}
http {
# 文件类型定义
include mime.types;
# 传输的content-type
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# 文件传输优化
sendfile on;
# tcp传输优化,需要sendfile为on
#tcp_nopush on;
# 每个请求存活时间
#keepalive_timeout 0;
keepalive_timeout 65;
# 压缩传输,可以节省带宽
gzip on;
# 配置压缩率
gzip_comp_level 3;
# 压缩文件类型
gzip_types text/plain application/javascript application/x-javascript text/css app lication/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json;
# 允许压缩的最小字节数 1kb
gzip_min_length 1024;
# 加入自定义的网页界面的配置 静态
#include frontend.conf;
# 加入自定义的tomcat配置 动态
#include tomcats.conf;
# 进行服务器配置实验
server {
listen 80;
server_name shop.mk-shop.com;
location / {
root /home/website/foodie-shop;
index index.html;
}
#location /mk-shop-api/ {
# add_header 'Access-Control-Allow-Origin' *;
# add_header 'Access-Control-Allow-Credentials' 'true';
# add_header 'Access-Control-Allow-Methods' *;
# add_header 'Access-Control-Allow-Headers' *;
# proxy_pass http://192.168.182.131:8088;
#}
}
server {
listen 80;
server_name center.mk-shop.com;
location / {
root /home/website/foodie-center;
index index.html;
}
}
# 配置集群
upstream api.mk-shop.com{
server 192.168.182.131:8088;
server 192.168.182.130:8088;
}
server {
listen 80;
server_name api.mk-shop.com;
#允许跨域请求的域,*代表所有
add_header 'Access-Control-Allow-Origin' '*';
#允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' 'true';
#允许请求的方法,比如 GET/POST/PUT/DELETE
add_header 'Access-Control-Allow-Methods' '*';
#允许请求的header
add_header 'Access-Control-Allow-Headers' '*';
location / {
proxy_pass http://api.mk-shop.com;
}
}
}
- 配置说明
server {
listen 80;#要监听的端口
server_name shop.mk-shop.com; #要监听的请求域名,这与实体机中配置的映射有关,此处实体机中映射到了135IP
# 通过映射域名访问80端口时,直接将存储在本地/home/website/foodie-shop中的index.html文件返回给客户端浏览器
location / {
root /home/website/foodie-shop;
index index.html;
}
server {
listen 80;
server_name center.mk-shop.com;
location / {
root /home/website/foodie-center;
index index.html;
}
}
Nginx跨域配置方法一
server {
listen 80;
server_name shop.mk-shop.com;
location / {
root /home/website/foodie-shop;
index index.html;
}
# 通过url映射的方式来实现请求
location /mk-shop-api/ {
proxy_pass http://192.168.182.131:8088;# 后端tomcat所在主机的IP
}
}
严格说来,这种方式并不算是跨域请求,因为这种配置要求后端请求的url必须为http://shop.mk-shop.com/mk-shop-api/xxxx的形式,也就是说前端项目的请求地址必须进行更改为和客户端页面访问时的url地址一样,这样貌似也就没有跨域问题了。如果前端项目不同模块的请求地址不一样,则都需要进行更改。
Nginx跨域配置方法二
# 配置集群
upstream api.mk-shop.com{
server 192.168.182.131:8088;
server 192.168.182.130:8088;
}
server {
listen 80;
server_name api.mk-shop.com;
#允许跨域请求的域,*代表所有, 有时候可以添加 always 参数
#使用 * 号存在一定的风险,可以使用固定的请求地址代替
add_header 'Access-Control-Allow-Origin' '*';
#允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' 'true';
#允许请求的方法,比如 GET/POST/PUT/DELETE
add_header 'Access-Control-Allow-Methods' '*';
#允许请求的header
add_header 'Access-Control-Allow-Headers' '*';
location / {
proxy_pass http://api.mk-shop.com;
}
}
注意:api.mk-shop.com是后端api的请求前缀,也就是客户端异步加载时的请求地址,以我的项目的后端请求地址为例:
http://api.mk-shop.com/mk-shop-api/index/carousel
后端跨域处理
SpringBoot后端处理跨域时可以配置过滤器来实现
@Configuration
public class CorsConfig {
public CorsConfig() {
}
@Bean
public CorsFilter corsFilter() {
//1.添加cors配置信息
CorsConfiguration config = new CorsConfiguration();
//设置允许的跨域来源
//发布时本机的域名需要加入到跨域设置中
//新版本的springboot中不允许在setAllowedOrigins包含 * ,需要使用setAllowedOriginPatterns代替
// List<String> allowedOrigins = Arrays.asList(
// "http://localhost:8080",
// "http://shop.mk-shop.com",
// "http://shop.mk-shop.com:8080",
// "http://center.mk-shop.com",
// "http://center.mk-shop.com:8080",
// "*"
// );
// config.setAllowedOrigins(allowedOrigins);
config.setAllowedOriginPatterns(Arrays.asList("*"));
//设置是否允许携带cookie
config.setAllowCredentials(true);
//设置允许的header
config.setAllowedHeaders(Arrays.asList("*"));
//设置允许的方法
config.setAllowedMethods(Arrays.asList("*"));
//2.为url添加请求路径
UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
//所有路径都转发
corsSource.registerCorsConfiguration("/**", config);
//3.返回重新定义好的corsSource
return new CorsFilter(corsSource);
}
注意:使用过程中,新版本的springboot可能会报错不允许在setAllowedOrigins包含 * ,此时需要使用setAllowedOriginPatterns代替。
最后一点!!
- Nginx和后端配置不要同时使用!!
- 同时使用貌似会出错,搞死我了!