在同一 VPS 上运行的前端和后端的 CORS 问题

问题描述 投票:0回答:1

我的 VPS 上的设置如下:

  • golang 后端,在 127.0.0.1:6060 上的 docker 容器中公开 REST API
  • svelte 前端,使用节点适配器构建,在 Node.js 进程中运行,在 127.0.0.1:3000 上
  • nginx 作为反向代理,使用 certbot 进行 https

我使用一个域名(我们称之为

https://example.com
),它通过 DNS A 记录重定向到我的 VPS IP。到目前为止一切顺利,前端正在我的域上运行。

我的nginx设置基本上是这些:

server {
        server_name example.com www.example.com;

        location / {
                proxy_pass http://localhost:3000/;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
        }
}

加上 certbot 的东西。

我使用 js fetch() 请求来调用我的 golang REST API。请求的来源是

https://example.com

在我的后端 http 服务器中,我设置了以下标头来处理 CORS:

headers.Add("Access-Control-Allow-Credentials", "true")
headers.Add("Access-Control-Allow-Origin", "https:example.com")
headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
if req.Method != http.MethodOptions {
    next.ServeHTTP(rw, req)
    return
}
// process an HTTP OPTIONS preflight request
headers.Add("Access-Control-Allow-Headers", "content-type, allow-credentials")

我还使用 autocert 包的管理器来处理 https,设置如下所示:

m := &autocert.Manager{
        Cache:      autocert.DirCache("secret-dir"),
        Prompt:     autocert.AcceptTOS,
        Email:      "[email protected]",
        HostPolicy: autocert.HostWhitelist("example.com", "www.example.com"),
    }

    server := &http.Server{
        // Addr: ":6060",
        Addr:      ":https",
        TLSConfig: m.TLSConfig(),
        // Set timeouts to avoid Slowloris attacks.
        WriteTimeout: time.Second * 15,
        ReadTimeout:  time.Second * 15,
        IdleTimeout:  time.Second * 60,
        Handler:      routerWithCORS,
    }

我的 GET(等)请求调用

https://127.0.0.1:6060/api/v1/whatever
,调用的来源是
https://example.com
,在浏览器的网络选项卡中我得到

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://127.0.0.1:6060/api/v1/me. (Reason: CORS request did not succeed). Status code: (null).

我还应用了这个开放的 nginx 设置来在 nginx 中启用 CORS,但没有成功:

location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
     }
}

我以为我的 golang 后端不需要 https,因为请求来自 VPS 上的 127.0.0.1 并在同一 VPS 上调用 127.0.0.1:6060,但查看网络选项卡,调用的来源是https://example.com。所以我想这是主要问题。我无法从 https 源调用 http 资源。但即使在 https 中运行后端也不起作用。

我希望这个问题可能有一个不太复杂的解决方案,希望您能帮助我指明正确的方向。

go cors network-programming fetch-api vps
1个回答
0
投票

最多在一处配置 CORS

通过在多个地方配置CORS,

  • 在应用程序级别(在您的 Go 代码中),并且
  • 在反向代理级别(在您的 NGINX 配置中),

你正在引发问题。 CORS 最多应配置在一个地方,只要它更容易并且配置更改更有可能得到审查。

赞成在应用程序级别配置 CORS

那么应该在哪里配置CORS呢? 在 NGINX 等反向代理中配置 CORS 可能具有挑战性,因为您无法获得 Go 等编程语言的完整表达能力。这就是为什么我偏向于在应用程序级别配置 CORS。

依赖经过验证的 CORS 中间件

通过手动设置响应标头来配置 CORS 很诱人,但很容易出错,尤其是在您不太熟悉该协议的情况下。你的具体案例证明了我的观点:

headers.Add("Access-Control-Allow-Origin", "https:example.com")
headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
if req.Method != http.MethodOptions {
    next.ServeHTTP(rw, req)
    return
}
request headers.Add("Access-Control-Allow-Headers", "content-type, allow-credentials")
  • 第一个语句不可能按预期工作,因为

    https:example.com
    不是有效的 Web 来源;你的意思是
    https://example.com

  • 与普遍看法相反,您很可能不需要将

    OPTIONS
    列为允许的方法。

  • 另一个问题是标头

    Access-Control-Allow-Methods
    在响应非预检请求时没有位置。

  • 此外,并非所有

    OPTIONS
    请求都是预检请求。

所有这些困难应该足以让您相信,您最好依赖经过验证的 CORS 中间件库,它可以从您身上抽象出一些复杂性。

使用 jub0bs/fcors 的解决方案

我最近发布了 jub0bs/fcors,一个 CORS 中间件库,其设计易于使用,而且重要的是,很难误用。这是一个例子:

cors, err := fcors.AllowAccessWithCredentials(
  fcors.FromOrigins("https://example.com"),
  fcors.WithMethods(
    http.MethodGet,
    http.MethodPost,
  ),
  fcors.WithRequestHeaders("Content-Type"),
)
if err != nil {
  log.Fatal(err)
}
// apply cors to your http.Handler
// start your server

(仅供参考,名为

cors
的结果具有类型
func(http.Handler) http.Handler
。)

调整此示例以满足您的需求,并删除 NGINX 配置中与 CORS 相关的所有内容。

© www.soinside.com 2019 - 2024. All rights reserved.