Title: 使用nginx配置CORS-跨域请求访问
Date: 2019-3-23 12:30
Category: 技术博客
Modified: 2019-3-23 12:30
Tags: CORS, Nginx
Slug: CORS-with-Nginx
Authors: Victor Lv
Summary: 前后端分离的web系统,存在 CORS–跨域(协议 / 域名 / 端口)请求访问的问题。本文通过在 Nginx 上设置反向代理并配置 CORS header,解决 CORS 的问题。

CORS 定义

前后端分离的web系统,存在 CORS–跨域(协议 / 域名 / 端口)请求访问的问题。

关于 CORS 的定义,参考网上的帖子:

“出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。”

这里以我使用的架构作例子:
前端:使用 Vue.js ,通过 nodeJS 部署,假设地址为 http://172.17.1.1:8080;
后端:使用SpringMVC,部署在 web 容器上,假设地址为 http://172.17.2.2:10000

前端通过浏览器向后端发送 http 请求时,请求能被后端接收,但返回给浏览器时,浏览器判断出应答的域(172.17.2.2:10000)与当前的域不一致,会拒绝,页面报404错误。

解决方案

解决办法就是给应答的 http 报文添加 CORS 请求头(诸如 Access-Control-Allow-Origin 等配置)。

而添加 http 应答 CORS 头这个动作,可以选用多种方式。比如:
方式1:后端(SpringMVC)添加 CORS 配置;
方式2:浏览器安装 postman 插件自动修改(篡改)http 应答,添加 CORS 头信息;
方式2:使用 Nginx / Apache 服务器作为代理,添加 CORS 头信息,其实 postman 也相当于代理。

针对我个人的实际情况,因为系统上线生产实际要讲前端编译好的静态文件部署到后端工程中(也就是不分离),只在开发阶段为了提升效率采用前后端分离,那么方式1在后端添加的代码对于生产应用来说就是无用代码,多少存在些隐患,所以方式1不适用于我。方式2,每个开发者都得安装这个浏览器插件,麻烦。于是,我采用了 Nginx 服务器作为反向代理的方式。

使用 Nginx 搭建反向代理,配置 CORS

  1. 首先下载 Nginx ,有 windows 版也有 linux 版(下面的配置都通用),我在 linux 服务器上常挂一个 Nginx 服务,这样前端请求的后端地址,直接写成 Nginx 的服务地址即可。
  2. 在 Nginx.conf 上添加反向代理配置,指向后端服务地址:
    在 server-location 中添加一项配置即可:proxy_pass http://172.17.1.1:10000/;
  3. 添加 CORS 配置,参考如下:
#CORS 配置
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
#是否允许cookie传输
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Data-Type,X-Requested-With,X-Data-Type,X-Auth-Token';
            
 #针对浏览器的options预请求直接返回200,否则会被403 forbidden--invalie CORS request
if ( $request_method = 'OPTIONS' ) { 
		return 200;
} 

逐个配置项解析下:
(1)Access-Control-Allow-Origin配置的是允许的 origin 地址,* 表示通配,所有源地址都接收,也可以采用粒度更细的控制,输入具体的几个 http:ip:port 地址。
(2)Access-Control-Allow-Methods 表示允许的 http 动词,也可以通配。
(3)Access-Control-Allow-Credentials( true / false )表示是否允许 cookie 传输。
(4)Access-Control-Allow-Headers响应首部 Access-Control-Allow-Headers 用于 preflight request (预检请求)中,列出了将会在正式请求的 Access-Control-Expose-Headers 字段中出现的首部信息。如 simple headers、Accept、Accept-Language、Content-Language、**Content-Type **。这里比较常用的是 Content-Type 信息,其值比如有最常见的 post 提交数据的方式,浏览器原生的 form 表单:application/x-www-form-urlencoded;比如越来越流行的application/json
(5)在我的项目中,有了以上四个配置项还不够,因为对于 post 请求,浏览器会首先发一个 options请求,向服务端询问并协商好诸如content-type等一些参数,然后才是发真正的 post 请求。而我的 SpringMVC 并不能映射这个 options 请求,导致请求出错(F12 观察到报错:403 forbidden–invalie CORS request)。经过一番研究,在 Nginx 上加入针对 OPTIONS的特殊配置即可解决,也就是如上的配置,判断如果request_methodOPTIONS的话,直接返回 http-200(成功),不需要再转发给 web 容器处理,这样 SpringMVC 接收到的就只有真正的 post 请求了。

综上,得出 nginx.conf 全文配置如下:

nginx.conf


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;

pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    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_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       1234;
        server_name  localhost;

        #charset koi8-r;

        access_log  logs/host.access.log  main;

        #反向代理
        location / {
            proxy_pass  http://172.17.1.1:10000/;
			
			#CORS 配置
            add_header 'Access-Control-Allow-Origin' '*';
			add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
			#是否允许cookie传输
            add_header 'Access-Control-Allow-Credentials' 'true';
			add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Data-Type,X-Requested-With,X-Data-Type,X-Auth-Token';
            
            #针对浏览器的options预请求直接返回200,否则会被403 forbidden--invalie CORS request
            if ( $request_method = 'OPTIONS' ) { 
								return 200;
						} 
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

Nginx 配置生效

如果在 Nginx 启动时修改 Nginx.conf ,并不需要先关闭 Nginx 修改配置后再打开,只需要执行以下两句命令把配置生效一下即可:

nginx -t
nginx -s reload

nginx -t 是重读 conf 配置, nginx -s reaload 是将配置重新载入运行区。

另附 SpringMVC 中加入的 CORS 配置

只需要在 scan 的 package 中加入以下配置类即可(采用Spring-WebMVC 5.1.3 版本编写):
CORSConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import static org.springframework.web.cors.CorsConfiguration.ALL;

/**
 * CORS configuration
 * 用于开放跨域资源共享,
 * 解决 No Acces-Control-Allow-Origin 的报错
 */
@Configuration
public class CORSConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
//                .allowedOrigins("http://127.0.0.1:8080")
                //TODO 需要进一步细化权限颗粒度
                .allowedOrigins(ALL)
                .allowedMethods(ALL)
                .allowedHeaders(ALL)
                .allowCredentials(true);
    }
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐