Dans le développement Web, l'authentification est une fonctionnalité essentielle et l'authentification basée sur les cookies est un moyen courant. En tant que langage de programmation efficace et concis, Golang dispose de puissantes capacités de développement Web. Cet article expliquera comment utiliser la boîte à outils Gorilla pour implémenter la fonction Websocket avec authentification par cookie dans Golang, rendant votre application plus sécurisée et fiable. Que vous soyez un débutant Golang ou un développeur expérimenté, cet article peut vous aider à démarrer rapidement. Nous allons jeter un coup d'oeil!
J'essaie d'utiliser Gorilla Websocket pour lancer un graphique. Le middleware d'authentification fonctionne via des cookies et des jetons jwt. Tous mes points de terminaison via http fonctionnent, mais pas Websocket. Après avoir lu de nombreux sujets comme Gorilla Websocket avec authentification par cookie, j'ai découvert que mes cookies étaient vides et que le contexte de la connexion Websocket était également vide. Je ne comprends pas pourquoi? Quelqu'un peut-il expliquer pourquoi ? p.s .: J'ai essayé de supprimer la mise à niveau de ce gestionnaire et le cookie et le contexte ont réussi, mais après la mise à niveau de la connexion au protocole websocket, cela a échoué. Voici mon fichier : Point final :
func (r *router) routes(engine *gin.engine) { engine.use(r.handler.verifyuser()) engine.post("/signup", r.handler.createuser) engine.post("/signin", r.handler.loginuser) engine.get("/welcome", r.handler.welcome) engine.get("/logout", r.handler.logout) engine.post("/ws/createroom", r.wshandler.createroom) engine.get("/ws/joinroom/:roomid", r.wshandler.joinroom) }
ws_handler
func (h *handler) joinroom(c *gin.context) { claims := c.request.context().value("jwt").(models.claims) //couldn't find value with "jwt" key fmt.println(claims.id, claims.name) cookie, err := c.cookie("chartjwt") // allways err no cookie if err != nil { fmt.printf("no cookie, error:%v\n", err) } fmt.printf("cookie: %+v\n", cookie) conn, err := upgrader.upgrade(c.writer, c.request, nil) if err != nil { c.json(http.statusbadrequest, gin.h{"error": err.error()}) return }
Middleware :
func (h *handler) verifyuser() gin.handlerfunc { return func(c *gin.context) { notauth := []string{"/signup", "/signin"} requestpath := c.request.url.path for _, val := range notauth { if val == requestpath { c.next() return } } token, err := c.cookie("chartjwt") if err != nil { c.redirect(http.statuspermanentredirect, signinpage) } claims, ok := validatetoken(token) if !ok { c.json(http.statusbadrequest, gin.h{"error": errors.new("invalid token")}) return } c.request = c.request.withcontext(context.withvalue(c.request.context(), "jwt", *claims)) c.next() } }
Tous les autres points de terminaison fonctionnent, si vous avez besoin d'un autre code, veuillez me le faire savoir. Je ne veux pas compliquer mon problème car je pensais que c'était simple mais j'ai mal compris quelque chose ( Merci pour votre aide et vos conseils.
ps. : Si je désactive le middleware, tout fonctionne comme prévu.
Mise à jour : Ajout de fonctions de validation et de génération
func validatetoken(jwttoken string) (*models.claims, bool) { claims := &models.claims{} token, err := jwt.parsewithclaims(jwttoken, claims, func(token *jwt.token) (interface{}, error) { return []byte(config.secretkey), nil }) if err != nil { return claims, false } if !token.valid { return claims, false } return claims, true } func (h *handler) generatetokenstringforuser(id, name string) (string, error) { // create the jwt claims, which includes the username and expiry time claims := models.claims{ id: id, name: name, registeredclaims: jwt.registeredclaims{ issuer: id, expiresat: jwt.newnumericdate(time.now().add(24 * time.hour)), }, } token := jwt.newwithclaims(jwt.signingmethodhs256, claims) tokenstring, err := token.signedstring([]byte(config.secretkey)) return tokenstring, err }
Ajout d'une fonctionnalité de connexion où j'ai ajouté un cookie avec une chaîne jwt
func (h *handler) LoginUser(c *gin.Context) { var input models.LoginUserReq if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } res, err := h.Service.LoginUser(context.Background(), &input) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } token, err := h.generateTokenStringForUser(res.ID, res.Name) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.SetCookie("chartJWT", token, 60*60*24, "/", "localhost", false, true) c.JSON(http.StatusOK, gin.H{"user": res}) }
Mise à jour après aide : Le problème était que dans ma demande de configuration du facteur, je n'avais pas spécifié correctement le cookie.
Laissez-moi essayer de vous aider à résoudre le problème. Tout d’abord, j’ai un peu simplifié votre exemple pour me concentrer uniquement sur les parties pertinentes. Si vous devez omettre quelque chose, faites-le-moi savoir et je mettrai à jour la réponse. Tout d’abord, permettez-moi de commencer par la génération/validation de jetons locaux auth
包中进行的 jwt
.
auth/auth.go
Fichierspackage auth import ( "fmt" "strings" "time" "github.com/golang-jwt/jwt" ) func validatetoken(jwttoken string) (*jwt.mapclaims, error) { // parse the token token, err := jwt.parse(strings.replace(jwttoken, "bearer ", "", 1), func(token *jwt.token) (interface{}, error) { _, ok := token.method.(*jwt.signingmethodhmac) if !ok { return nil, fmt.errorf("unexpected signing method: %v", token.header["alg"]) } return []byte("abcd1234!!"), nil }) // err while parsing the token if err != nil { return nil, err } // token valid var claims jwt.mapclaims var ok bool if claims, ok = token.claims.(jwt.mapclaims); ok && token.valid { return &claims, nil } return nil, fmt.errorf("token not valid") } func generatetoken(username, password string) (string, error) { // todo: here you can add logic to check against a db //... // create a new token by providing the cryptographic algorithm token := jwt.new(jwt.signingmethodhs256) // set default/custom claims claims := token.claims.(jwt.mapclaims) claims["exp"] = time.now().add(24 * time.hour * 3).unix() claims["username"] = username claims["password"] = password tokenstring, err := token.signedstring([]byte("abcd1234!!")) if err != nil { return "", err } return tokenstring, nil }
Maintenant, passons à la partie middleware.
middlewares/middlewares.go
Fichierspackage middlewares import ( "net/http" "websocketauth/auth" "github.com/gin-gonic/gin" ) func verifyuser() gin.handlerfunc { return func(c *gin.context) { notauth := []string{"/signin"} requestpath := c.request.url.path for _, val := range notauth { if val == requestpath { c.next() return } } token, err := c.cookie("chartjwt") if err != nil { c.redirect(http.statuspermanentredirect, "/signin") } claims, err := auth.validatetoken(token) if err != nil { c.json(http.statusbadrequest, gin.h{"error": err.error()}) return } c.set("jwt", *claims) c.next() } }
Pour pouvoir télécharger quelque chose en contexte, vous devez utiliser la méthode c.set(key, value)
. Passons maintenant aux gestionnaires.
handlers/handlers.go
Fichierspackage handlers import ( "fmt" "net/http" "websocketauth/auth" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "github.com/gorilla/websocket" ) var upgrader websocket.upgrader type loginuserreq struct { username string `json:"username" binding:"required"` password string `json:"password" binding:"required"` } func loginuser(c *gin.context) { var input loginuserreq if err := c.shouldbind(&input); err != nil { c.json(http.statusbadrequest, gin.h{"error": err.error()}) return } // i don't know what you do within the handler.service.loginuser() method token, err := auth.generatetoken(input.username, input.password) if err != nil { c.json(http.statusbadrequest, gin.h{"error": err.error()}) return } c.setcookie("chartjwt", token, 60*60*24, "/", "localhost", false, true) c.json(http.statusok, gin.h{"user": token}) } func joinroom(c *gin.context) { claims := c.mustget("jwt").(jwt.mapclaims) fmt.println("username", claims["username"]) fmt.println("password", claims["password"]) ws, err := upgrader.upgrade(c.writer, c.request, nil) if err != nil { panic(err) } charttoken, err := c.cookie("chartjwt") if err != nil { panic(err) } fmt.println("charttoken", charttoken) _ = ws }
Les parties manquantes comme les méthodes handler.service.loginuser()
方法。要正确地从上下文中读取内容,您必须使用 c.mustget(key)
sont ignorées car je ne sais pas ce qu'elles font.
main.go
Fichierspackage main import ( "websocketauth/handlers" "websocketauth/middlewares" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) func main() { handlers.Upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } gin.SetMode(gin.DebugMode) r := gin.Default() r.Use(middlewares.VerifyUser()) r.GET("/join-room", handlers.JoinRoom) r.POST("/signin", handlers.LoginUser) r.Run(":8000") }
C'est la logique de configuration. Rien à mentionner ici.
Si vous avez besoin d'aide supplémentaire, n'hésitez pas à me le faire savoir, merci !
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!