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

fnatzsnv  于 2023-03-10  发布在  Go
关注(0)|答案(1)|浏览(125)

我在VPS上的设置如下:

  • golang后端,在www.example.com上的Docker容器中公开REST API127.0.0.1:6060
  • 苗条的前端,使用节点适配器构建,在Node.js进程中运行,网址为127.0.0.1:3000
  • nginx作为反向代理,使用certbot for 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。
在我的后端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:      "example@example.org",
        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,因为请求来自127.0.0.1VPS上的www.example.com,并且在同一个VPS上调用127.0.0.1:6060,但是查看网络选项卡,调用的来源是https://example.com。所以我想这是主要问题。我不能从https来源调用http资源。但是即使在https中运行后端也不起作用。
我希望能有一个不那么复杂的解决办法来解决这个问题,希望能帮助我找到正确的方向。

dluptydi

dluptydi1#

最多在一个位置配置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 } // process an HTTP OPTIONS preflight request headers.Add("Access-Control-Allow-Headers", "content-type,
allow-credentials")
  • 由于https:example.com不是有效的Web origin,因此第一条语句不可能按预期工作;你是说https://example.com
  • 与流行的看法相反,您很可能不需要将OPTIONS列为允许的方法。
  • 另一个问题是标头Access-Control-Allow-Methods在响应非预检请求时没有位置。
  • 此外,并非所有OPTIONS请求都是印前检查请求。

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

使用jub 0 bs/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

(For信息,名为cors的结果具有类型func(http.Handler) http.Handler。)
调整此示例以满足您的需要,并删除NGINX配置中与CORS相关的所有内容。

相关问题