CORS预检请求在使用gorilla/handlers时出现了神秘的失败。

huangapple go评论99阅读模式
英文:

CORS preflight mysteriously failing with gorilla/handlers

问题

我正在通过Heroku发布我的应用程序的Golang API。无法使我的Web应用程序(Flutter/Dart堆栈)从我的API获得成功响应。但是,我可以使用本地的curl命令获得成功的响应。我已经阅读了几篇关于修改Go mux服务器并添加正确标头的帖子,但对我没有起作用。我甚至在我的curl请求中看到了这些标头返回。希望能得到一些帮助,因为这让我进展缓慢。

这是创建服务器的主要类:

import (
	"api/preventative_care"
	"api/user"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"os"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Llongfile)
	router := mux.NewRouter()

	// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)

	headersOk := handlers.AllowedHeaders([]string{"*"})
	methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
	originsOk := handlers.AllowedOrigins([]string{"*"})

	router.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		log.Println("Up and running!")
	})
	router.HandleFunc("/api/login", user.LoginHandler).Methods("GET")
	router.HandleFunc("/api/recommendations", preventative_care.RecommendationHandler).Methods("GET")

	var port = os.Getenv("PORT")
	log.Printf("Starting application on port %s\n", port)

	//log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), router))
	log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))

}

调用此API的Dart代码如下:

    Map<String, String> headers = {
      "content-type": "application/json",
      "username": username,
      "password": password
    };

    final response = await http.get(Uri.parse(uri), headers: headers);

我将Web应用程序和API托管在两个单独的Heroku Dynos中。当我使用curl从本地访问API时,我看到以下内容:

$ > curl -iXGET https://my-app-api.herokuapp.com/api/login -H "username:hello" -H "password:pizza"

HTTP/1.1 200 OK
Server: Cowboy
Connection: keep-alive
Content-Type: application/json
Date: Thu, 18 Nov 2021 23:39:56 GMT
Content-Length: 160
Via: 1.1 vegur

我以为我应该看到添加了Access-Control-Allow-Origin: *标头,但实际上没有,但我仍然得到了200的成功响应。然而,当我尝试使用我的Web应用程序在Google Chrome中的登录界面访问API时,我看到以下错误:

访问'https://my-app-api.herokuapp.com/api/login'的XMLHttpRequest被来自'https://my-app-staging.herokuapp.com'的源阻止,因为CORS策略:预检请求的响应未通过访问控制检查:所请求的资源上不存在'Access-Control-Allow-Origin'标头。

不知道为什么-就好像Chrome删除了标头或其他什么东西?

编辑:我还尝试使用CURL发送预检请求,我看到了正确的标头-但仍然显示4XX错误。

$ > curl -H "Access-Control-Request-Method: GET" -H "Origin: https://my-app-staging.herokuapp.com" --head https://my-app-api.herokuapp.com/api/login

HTTP/1.1 405 Method Not Allowed
Server: Cowboy
Connection: keep-alive
Access-Control-Allow-Origin: *
Date: Fri, 19 Nov 2021 00:51:52 GMT
Via: 1.1 vegur

所以现在我真的不确定了。

英文:

I am publishing a Golang API for my application through Heroku. Not able to get my webapp (Flutter/Dart stack) to actually get a successful response from my api. However I am able to get a successful response using curl commands from local. I've read through several posts about altering the Go mux server and adding the correct headers but this has not worked for me. I even see these headers returned back during my curl requests. Could really use some help as this is slowing me down.

essentially this is my main class which creates the server

import (
	&quot;api/preventative_care&quot;
	&quot;api/user&quot;
	&quot;github.com/gorilla/handlers&quot;
	&quot;github.com/gorilla/mux&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;os&quot;
)

func main() {
	log.SetFlags(log.LstdFlags | log.Llongfile)
	router := mux.NewRouter()

	// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)

	headersOk := handlers.AllowedHeaders([]string{&quot;*&quot;})
	methodsOk := handlers.AllowedMethods([]string{&quot;GET&quot;, &quot;HEAD&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;OPTIONS&quot;})
	originsOk := handlers.AllowedOrigins([]string{&quot;*&quot;})

	router.HandleFunc(&quot;/&quot;, func(writer http.ResponseWriter, request *http.Request) {
		log.Println(&quot;Up and running!&quot;)
	})
	router.HandleFunc(&quot;/api/login&quot;, user.LoginHandler).Methods(&quot;GET&quot;)
	router.HandleFunc(&quot;/api/recommendations&quot;, preventative_care.RecommendationHandler).Methods(&quot;GET&quot;)

	var port = os.Getenv(&quot;PORT&quot;)
	log.Printf(&quot;Starting application on port %s\n&quot;, port)

	//log.Fatal(http.ListenAndServe(fmt.Sprintf(&quot;:%s&quot;, port), router))
	log.Fatal(http.ListenAndServe(&quot;:&quot; + os.Getenv(&quot;PORT&quot;), handlers.CORS(originsOk, headersOk, methodsOk)(router)))

}

And the dart code which calls this API looks like this:

    Map&lt;String, String&gt; headers = {
      &quot;content-type&quot;: &quot;application/json&quot;,
      &quot;username&quot;: username,
      &quot;password&quot;: password
    };

    final response = await http.get(Uri.parse(uri), headers: headers);

I'm hosting both the webapp and the API in 2 separate Heroku Dynos. When I hit the API from my local using curl I see below:

$ &gt; curl -iXGET https://my-app-api.herokuapp.com/api/login -H &quot;username:hello&quot; -H &quot;password:pizza&quot;

HTTP/1.1 200 OK
Server: Cowboy
Connection: keep-alive
Content-Type: application/json
Date: Thu, 18 Nov 2021 23:39:56 GMT
Content-Length: 160
Via: 1.1 vegur

I thought I was supposed to see see the Header Access-Control-Allow-Origin: * added there but it's not yet I still get 200 success back. However when I try to use my Webapp to hit the API from a login screen using Google Chrome I see this error:

> Access to XMLHttpRequest at 'https://my-app-api.herokuapp.com/api/login' from origin 'https://my-app-staging.herokuapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

No idea - it's like the header is being removed by Chrome or something maybe?

EDIT: I've also tried sending a preflight request using CURL and I am seeing the correct headers - however it's still showing me 4XX error.

$ &gt; curl -H &quot;Access-Control-Request-Method: GET&quot; -H &quot;Origin: https://my-app-staging.herokuapp.com&quot; --head https://my-app-api.herokuapp.com/api/login

HTTP/1.1 405 Method Not Allowed
Server: Cowboy
Connection: keep-alive
Access-Control-Allow-Origin: *
Date: Fri, 19 Nov 2021 00:51:52 GMT
Via: 1.1 vegur

So now I'm REALLY not sure

答案1

得分: 4

TL;DR

gorilla/handlers目前不支持通配符Access-Control-Allow-Headers。您必须明确指定所有允许的标头。在您的情况下,您应该使用以下代码:

handlers.AllowedHeaders([]string{"content-type", "username", "password"})

更多细节

Fetch标准在2016年(对于非凭证请求)添加了对Access-Control-Allow-Headers标头中通配符的支持。现代大多数浏览器现在都支持此功能

然而,gorilla/handlers似乎还没有跟上规范。如果您检查handlers.AllowedHeaders函数*cors.ServeHTTP方法的源代码,您会发现没有对"*"值进行特殊处理:它被当作字面值处理。因此,CORS中间件会在您的预检请求提供的请求标头(content-typeusernamepassword)与您允许的标头(*,字面意思)之间检测到不匹配,并且响应一个403,甚至没有设置Access-Control-Allow-Origin标头,从而导致访问控制检查失败。

英文:

TL;DR

gorilla/handlers doesn't (yet?) support the wildcard for Access-Control-Allow-Headers. You must specify all the allowed headers explicitly. In your case, instead of

handlers.AllowedHeaders([]string{&quot;*&quot;})

you should have

handlers.AllowedHeaders([]string{&quot;content-type&quot;, &quot;username&quot;, &quot;password&quot;})

More details

The Fetch standard added support (in the case of non-credentialed requests) for the wildcard in the Access-Control-Allow-Headers header back in 2016. Most modern browsers now support this feature.

However, gorilla/handlers doesn't seem to have caught up with the spec yet. If you inspect the source code for the handlers.AllowedHeaders function and for the *cors.ServeHTTP method, you'll see that there's no special handling of the &quot;*&quot; value: it's treated literally. As a result, the CORS middleware detects a mismatch between the request headers supplied by your preflight request (content-type, username, and password) and your allowed headers (*, taken literally) and responds with a 403 without even setting the Access-Control-Allow-Origin header, thereby causing the access-control check to fail.

huangapple
  • 本文由 发表于 2021年11月19日 08:08:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/70028294.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定