我在聊天應用程式中遇到基於 cookie 的令牌驗證問題。我正在使用帶有標準網路庫的 go 後端來將令牌新增至回應 cookie。當使用者通過密碼驗證(透過 post 到身份驗證伺服器上的 /login 路徑)時,回應 cookie 應包含用於產生 api 令牌的存取權杖和用於重新產生存取權杖的刷新令牌。
這是一個標記文件,其中包含我的開發環境中應用程式服務的結構。每個伺服器都在本機上使用 go net/http 在順序連接埠上執行(不顯示不相關的服務)。
auth_server ( dependencies [] url (scheme "http" domain "localhost" port "8081") listenaddress ":8081" endpoints ( /jwtkeypub ( methods [get] ) /register ( methods [post] ) /logout ( methods [post] ) /login ( methods [post] ) /apitokens ( methods [get] ) /accesstokens ( methods [get] ) ) jwtinfo ( issuername "auth_server" audiencename "auth_server" ) ) message_server ( dependencies [auth_server] url (scheme "http" domain "localhost" port "8083") listenaddress ":8083" endpoints ( /ws ( methods [get] ) ) jwtinfo ( audiencename "message_server" ) ) static ( dependencies [auth_server, message_server] url (scheme "http" domain "localhost" port "8080") listenaddress ":8080" )
這是在登入時設定 cookie 的程式碼。密碼檢查後會發生這種情況
// set a new refresh token refreshtoken := s.jwtissuer.stringifyjwt( s.jwtissuer.minttoken(userid, s.jwtissuer.name, refreshtokenttl), ) kit.sethttponlycookie(w, "refreshtoken", refreshtoken, int(refreshtokenttl.seconds())) // set a new access token accesstoken := s.jwtissuer.stringifyjwt( s.jwtissuer.minttoken(userid, s.jwtaudience.name, accesstokenttl), ) kit.sethttponlycookie(w, "accesstoken", accesstoken, int(accesstokenttl.seconds())) }
func sethttponlycookie(w http.responsewriter, name, value string, maxage int) { http.setcookie(w, &http.cookie{ name: name, value: value, httponly: true, maxage: maxage, }) }
以下是當使用者請求 api 令牌時我如何存取 cookie。如果傳回錯誤,處理程序將呼叫 gettokenfromcookie() 函數並以 401 回應。這種情況下的錯誤是「http:命名的cookie不存在」
func gethttpcookie(r *http.request, name string) (*http.cookie, error) { return r.cookie(name) } func gettokenfromcookie(r *http.request, name string) (jwt.jwt, error) { tokencookie, err := gethttpcookie(r, name) if err != nil { // debug log.println(err) return jwt.jwt{}, err } return jwt.fromstring(tokencookie.value) }
來自登入端點的 200 回應後,頁面會重新導向到主應用程式頁面。在此頁面上,向身份驗證伺服器發出請求以接收用於連接即時聊天訊息伺服器的 api 令牌。從auth伺服器上的日誌輸出可以看到,請求中沒有收到存取權杖cookie,因此請求回傳401代碼。
2023/05/19 02:33:57 get [/jwtkeypub] - 200 2023/05/19 02:33:57 get [/jwtkeypub] - 200 2023/05/19 02:34:23 post [/login] - 200 2023/05/19 02:34:23 http: named cookie not present {{ } { } []} http: named cookie not present 2023/05/19 02:34:23 get [/apitokens?aud=msgservice] - 401
我相信問題在於我使用的是 localhost,而且瀏覽器不會將 cookie 從 locahost:8080 傳送到 localhost:8081。我正計劃實現某種模擬身份驗證,繞過讀取開發環境的 cookie 來解決這個問題,但我不確定這是否真的是我的問題的原因。只是想再看一下,看看我是否可以讓它工作而不需要這樣做。
更新:我已經查看了開發工具中的網路標籤: 圖像顯示登入後的回應傳回了 cookie,但它們隨後不會發送到連接埠 8081 上的身份驗證伺服器。在獲得登入的 200 回應後,我還查看了 cookie 存儲,即使在之後也沒有 cookie在回應中接收它們。我正在使用 firefox 的私人模式來訪問網站。請注意,即使我在 go 程式碼中設定了 maxage,cookie 也不包含 maxage,這似乎是一個問題。
更新:這是登入後的 har 檔案。您可以看到回應有 max-age,但之後它不會顯示在 cookies 標籤中。
{ "log": { "version": "1.2", "creator": { "name": "Firefox", "version": "113.0.1" }, "browser": { "name": "Firefox", "version": "113.0.1" }, "pages": [ { "startedDateTime": "2023-05-19T12:16:37.081-04:00", "id": "page_1", "title": "Login Page", "pageTimings": { "onContentLoad": -8105, "onLoad": -8077 } } ], "entries": [ { "pageref": "page_1", "startedDateTime": "2023-05-19T12:16:37.081-04:00", "request": { "bodySize": 31, "method": "POST", "url": "http://0.0.0.0:8081/login", "httpVersion": "HTTP/1.1", "headers": [ { "name": "Host", "value": "0.0.0.0:8081" }, { "name": "User-Agent", "value": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0" }, { "name": "Accept", "value": "*/*" }, { "name": "Accept-Language", "value": "en-US,en;q=0.5" }, { "name": "Accept-Encoding", "value": "gzip, deflate" }, { "name": "Referer", "value": "http://localhost:8080/" }, { "name": "Content-Type", "value": "text/plain;charset=UTF-8" }, { "name": "Content-Length", "value": "31" }, { "name": "Origin", "value": "http://localhost:8080" }, { "name": "DNT", "value": "1" }, { "name": "Connection", "value": "keep-alive" } ], "cookies": [], "queryString": [], "headersSize": 370, "postData": { "mimeType": "text/plain;charset=UTF-8", "params": [], "text": "{\"username\":\"a\",\"password\":\"a\"}" } }, "response": { "status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "headers": [ { "name": "Access-Control-Allow-Origin", "value": "*" }, { "name": "Set-Cookie", "value": "refreshToken=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJQMmY2RHg1RWxlYTF5THBUaVpEejBaS3Z1dk1FUkFPZEtBVGkwNDZSc2JNPSIsImF1ZCI6InN0ZWVsaXgiLCJpc3MiOiJzdGVlbGl4IiwiZXhwIjoiMTY4NTExNzc5NyIsImp0aSI6IjIwMUQzODZDNTRBQzlEOUMwRjdCODFBMDVDNDlFQTE1In0.SbxFgEAtZbh0zS-SXZmrVW9iLk-cFz6HcDMU0FHNl-K9BwCeb_boc5igEgImMSYK-NBVQZh1km7YknE-jkBWyF0rIYjSnTzjNUHHwMnn0jE1N-dtEfNRnF1OT0R2bxPSz8gmhtJ3B839xa-jh9uMPMkXEB8BYtABgPH1FqBdijHPUtRVKq6C3ulVleurp2eyF8EHpGLc9rr5wBYSFBk0HQ3FNjjUxfRQLDnzl2xYovoQ2em4grExnkdACxCSpXNtF5bQ7lCnEZyf7-CehrRNwZCpteGKj5ux_wrX_nxma3OEWwrlatML_j-e420TM1tub0C9Ymyt0bMugHw8vaiOGA; Max-Age=604800; HttpOnly" }, { "name": "Set-Cookie", "value": "accessToken=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJQMmY2RHg1RWxlYTF5THBUaVpEejBaS3Z1dk1FUkFPZEtBVGkwNDZSc2JNPSIsImF1ZCI6InN0ZWVsaXgiLCJpc3MiOiJzdGVlbGl4IiwiZXhwIjoiMTY4NDUxNDE5NyIsImp0aSI6IjY2NjU1QjAyNTc4NkRBRTE1M0VDNDI3MzBGMjMxQ0FGIn0.cIs6KGjRGTHaWX_uFTts_V2a3YcBb7LA0jNOBTZeyDmpPQgRlcABnuYkWUIdjUdR6VYnDitFRV-XK2ZSq6Pk_ZgyfvJ3yRzvWGYjXMu7Nq7MLpVvUh9mLKSbKvlqunW6YVamHSCAbYS8-D_pY9fpWxIcXw0qbwA2XfTdzr0Mrw7ntrkdyK7O1QqWamnEHCmpLfJ2XJlQsU0KaD8FjkL76pO3lWmrca3VYnTmjP1Oo1HEhbK3nImtrNeL2khAyb8ns8ROj2HX41IDNK1aHWPfn9J04pgH3AfBfcwhhqZkrKjTVFQAkSYzuvjKPWOfpgYmBMw3Y5nG_PDf-zlvVPrdpQ; Max-Age=1200; HttpOnly" }, { "name": "Date", "value": "Fri, 19 May 2023 16:16:37 GMT" }, { "name": "Content-Length", "value": "0" } ], "cookies": [ { "name": "refreshToken", "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJQMmY2RHg1RWxlYTF5THBUaVpEejBaS3Z1dk1FUkFPZEtBVGkwNDZSc2JNPSIsImF1ZCI6InN0ZWVsaXgiLCJpc3MiOiJzdGVlbGl4IiwiZXhwIjoiMTY4NTExNzc5NyIsImp0aSI6IjIwMUQzODZDNTRBQzlEOUMwRjdCODFBMDVDNDlFQTE1In0.SbxFgEAtZbh0zS-SXZmrVW9iLk-cFz6HcDMU0FHNl-K9BwCeb_boc5igEgImMSYK-NBVQZh1km7YknE-jkBWyF0rIYjSnTzjNUHHwMnn0jE1N-dtEfNRnF1OT0R2bxPSz8gmhtJ3B839xa-jh9uMPMkXEB8BYtABgPH1FqBdijHPUtRVKq6C3ulVleurp2eyF8EHpGLc9rr5wBYSFBk0HQ3FNjjUxfRQLDnzl2xYovoQ2em4grExnkdACxCSpXNtF5bQ7lCnEZyf7-CehrRNwZCpteGKj5ux_wrX_nxma3OEWwrlatML_j-e420TM1tub0C9Ymyt0bMugHw8vaiOGA" }, { "name": "accessToken", "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJQMmY2RHg1RWxlYTF5THBUaVpEejBaS3Z1dk1FUkFPZEtBVGkwNDZSc2JNPSIsImF1ZCI6InN0ZWVsaXgiLCJpc3MiOiJzdGVlbGl4IiwiZXhwIjoiMTY4NDUxNDE5NyIsImp0aSI6IjY2NjU1QjAyNTc4NkRBRTE1M0VDNDI3MzBGMjMxQ0FGIn0.cIs6KGjRGTHaWX_uFTts_V2a3YcBb7LA0jNOBTZeyDmpPQgRlcABnuYkWUIdjUdR6VYnDitFRV-XK2ZSq6Pk_ZgyfvJ3yRzvWGYjXMu7Nq7MLpVvUh9mLKSbKvlqunW6YVamHSCAbYS8-D_pY9fpWxIcXw0qbwA2XfTdzr0Mrw7ntrkdyK7O1QqWamnEHCmpLfJ2XJlQsU0KaD8FjkL76pO3lWmrca3VYnTmjP1Oo1HEhbK3nImtrNeL2khAyb8ns8ROj2HX41IDNK1aHWPfn9J04pgH3AfBfcwhhqZkrKjTVFQAkSYzuvjKPWOfpgYmBMw3Y5nG_PDf-zlvVPrdpQ" } ], "content": { "mimeType": "text/plain", "size": 0, "text": "" }, "redirectURL": "", "headersSize": 1347, "bodySize": 1748 }, "cache": {}, "timings": { "blocked": 0, "dns": 0, "connect": 0, "ssl": 0, "send": 0, "wait": 13, "receive": 0 }, "time": 13, "_securityState": "insecure", "serverIPAddress": "0.0.0.0", "connection": "8081" } ] } }
回應似乎有 cookie,但它們沒有被保存。
並且對身份驗證伺服器的下一個請求沒有新增任何 cookie。
tl;dr:
0.0.0.0
和 localhost
之間共用。 http://localhost:8080
和 http://localhost:8081
之間共用。 http://localhost:8080/
頁面傳送到 http://localhost:8081/
的請求將被視為跨網域請求。 fetch
發送的跨網域請求應使用 credentials: 'include'
進行初始化,以使瀏覽器保存 cookie。 har顯示網頁的url為http://localhost:8080/
,但登入端點為http://0.0.0.0:8081/login
。 0.0.0.0
的 cookie 不會與 localhost
共用。
您可以執行下面的示範來觀察行為:
執行示範:go run main.go
;
在瀏覽器中開啟http://localhost:8080/
。該網頁將執行以下操作:
http://0.0.0.0:8081/login1
發送請求(目的是驗證0.0.0.0
的cookie不會與localhost
共享;http://localhost:8081/login2
發送請求(目的是驗證會話cookie 將在http://localhost:8080
和http:/ /localhost:8081
之間共用;http://localhost:8081/login3
發送請求(目的是驗證正常的cookie將在http://localhost:8080
和http: //localhost:8081
之間共用;http://localhost:8080/resource
並且伺服器將轉儲請求。表示這個頭被傳送到伺服器:cookie:login2=localhost-session; login3=localhost
。 註解: credentials: 'include'
要求將 access-control-allow-origin
標頭設定為確切的來源(這表示*
將被拒絕),並且access- control-allow-credentials
標頭設定為true
。
package main import ( "fmt" "log" "net/http" "net/http/httputil" ) func setHeader(w http.ResponseWriter, cookieName, cookieValue string, maxAge int) { w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8080") w.Header().Set("Access-Control-Allow-Credentials", "true") http.SetCookie(w, &http.Cookie{ Name: cookieName, Value: cookieValue, MaxAge: maxAge, HttpOnly: true, }) } func main() { muxWeb := http.NewServeMux() // serve the HTML page. muxWeb.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := w.Write([]byte(page)) if err != nil { panic(err) } })) // Dump the request to see what cookies is sent to the server. muxWeb.Handle("/resource", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { dump, err := httputil.DumpRequest(r, false) if err != nil { panic(err) } _, _ = w.Write(dump) })) web := &http.Server{ Addr: ":8080", Handler: muxWeb, } go func() { log.Fatal(web.ListenAndServe()) }() muxAPI := http.NewServeMux() muxAPI.Handle("/login1", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { setHeader(w, "login1", "0.0.0.0", 1200) })) muxAPI.Handle("/login2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { setHeader(w, "login2", "localhost-session", 0) })) muxAPI.Handle("/login3", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { setHeader(w, "login3", "localhost", 1200) })) api := &http.Server{ Addr: ":8081", Handler: muxAPI, } go func() { log.Fatal(api.ListenAndServe()) }() fmt.Println("Open http://localhost:8080/ in the browser") select {} } var page string = ` <!DOCTYPE html> <html> <body> <script type="module"> async function login(url) { const response = await fetch(url, { mode: 'cors', credentials: 'include', }); } await login('http://0.0.0.0:8081/login1'); await login('http://localhost:8081/login2'); await login('http://localhost:8081/login3'); window.location = '/resource'; </script> </body> </html> `
以上是如何解決 Go 聊天應用程式中 cookie 未在本機主機連接埠之間傳輸的問題?的詳細內容。更多資訊請關注PHP中文網其他相關文章!