Je rencontre un problème avec l'authentification par jeton basée sur les cookies dans une application de chat. J'utilise le backend Go avec des bibliothèques réseau standard pour ajouter un jeton au cookie de réponse. Lorsqu'un utilisateur est authentifié par mot de passe (via une publication sur le chemin /login sur le serveur d'authentification), le cookie de réponse doit contenir le jeton d'accès utilisé pour générer le jeton API et le jeton d'actualisation utilisé pour régénérer le jeton d'accès.
Il s'agit d'un fichier de balisage qui contient la structure des services d'application dans mon environnement de développement. Chaque serveur s'exécute sur localhost en utilisant go net/http sur des ports séquentiels (les services non pertinents ne sont pas affichés).
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" )
C'est le code pour définir le cookie lors de la connexion. Cela se produit après la vérification du mot de passe
// 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, }) }
Voici comment j'accède au cookie lorsque l'utilisateur demande le jeton API. Si une erreur est renvoyée, le gestionnaire appelle la fonction gettokenfromcookie() et répond par 401. L'erreur dans ce cas est "http : Le cookie nommé n'existe pas"
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) }
Après la réponse 200 du point de terminaison de connexion, la page redirige vers la page principale de l'application. Sur cette page, faites une demande au serveur d'authentification pour recevoir le token API utilisé pour se connecter au serveur de messagerie de chat en direct. Comme vous pouvez le voir dans la sortie du journal sur le serveur d'authentification, le cookie du jeton d'accès n'a pas été reçu avec la requête, la requête a donc renvoyé un code 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
Je pense que le problème est que j'utilise localhost et que le navigateur ne transfère pas le cookie de locahost:8080 vers localhost:8081. Je prévois d'implémenter une sorte d'authentification par usurpation d'identité qui contourne la lecture des cookies de l'environnement de développement pour résoudre ce problème, mais je ne sais pas si c'est réellement la cause de mon problème. Je voulais juste jeter un autre coup d'œil et voir si je pouvais le faire fonctionner sans avoir à le faire.
Mise à jour : j'ai regardé l'onglet réseau dans les outils de développement : L'image montre que la réponse après la connexion renvoie des cookies, mais ils ne sont ensuite pas envoyés au serveur d'authentification sur le port 8081. Après avoir obtenu la réponse 200 pour la connexion, j'ai également examiné le stockage des cookies et même après cela, aucun cookie ne les a reçus dans la réponse. J'utilise le mode privé de Firefox pour accéder au site. Notez que même si j'ai défini maxage dans le code go, le cookie ne contient pas maxage, ce qui semble être un problème.
Mise à jour : il s'agit du fichier har après la connexion. Vous pouvez voir que la réponse a un âge maximum, mais elle n'apparaît pas dans l'onglet 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" } ] } }
La réponse semble contenir des cookies, mais ils ne sont pas enregistrés.
Et la prochaine requête au serveur d'authentification n'ajoute aucun cookie.
tl;dr:
0.0.0.0
和 localhost
. http://localhost:8080
和 http://localhost:8081
. http://localhost:8080/
页面发送到 http://localhost:8081/
seront traitées comme des demandes d'origine croisée. fetch
发送的跨域请求应使用 credentials: 'include'
Initialisez pour que le navigateur enregistre les cookies. har affiche l'url de la page Web pour http://localhost:8080/
,但登录端点为http://0.0.0.0:8081/login
。 0.0.0.0
的 cookie 不会与 localhost
le partage.
Vous pouvez exécuter la démo ci-dessous pour observer le comportement :
Lancez la démo : go run main.go
;
Ouvrir dans le navigateurhttp://localhost:8080/
. Cette page fera ce qui suit :
http://0.0.0.0:8081/login1
发送请求(目的是验证0.0.0.0
的cookie不会与localhost
;
http://localhost:8081/login2
(le but est de vérifier que le cookie de session sera partagé entre 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
Remarques : . 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> `
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!