Golang Websocket (Gorilla) mit Cookie-Authentifizierung

WBOY
Freigeben: 2024-02-11 18:03:08
nach vorne
1195 Leute haben es durchsucht

带 cookie 身份验证的 Golang Websocket (Gorilla)

In der Webentwicklung ist die Authentifizierung ein wesentliches Merkmal, und die Cookie-basierte Authentifizierung ist eine gängige Methode. Als effiziente und prägnante Programmiersprache verfügt Golang über leistungsstarke Webentwicklungsfunktionen. In diesem Artikel erfahren Sie, wie Sie mit dem Gorilla-Toolkit die Websocket-Funktion mit Cookie-Authentifizierung in Golang implementieren und so Ihre Anwendung sicherer und zuverlässiger machen. Egal, ob Sie ein Golang-Anfänger oder ein erfahrener Entwickler sind, dieser Artikel kann Ihnen den schnellen Einstieg erleichtern. Lass uns einen Blick darauf werfen!

Frageninhalt

Ich versuche, mit Gorilla Websocket ein Diagramm zu starten. Die Authentifizierungs-Middleware funktioniert über Cookies und JWT-Tokens. Alle meine Endpunkte über http funktionieren, Websocket jedoch nicht. Nachdem ich viele Themen wie Gorilla WebSocket mit Cookie-Authentifizierung gelesen hatte, stellte ich fest, dass meine Cookies leer waren und der Kontext in der WebSocket-Verbindung ebenfalls leer war. Ich verstehe nicht warum? Kann jemand erklären, warum? p.s.: Ich habe versucht, das Upgrade von diesem Handler zu entfernen, und das Cookie und der Kontext kamen erfolgreich durch, aber nach dem Upgrade der Verbindung auf das Websocket-Protokoll schlug es fehl. Das ist meine Datei: Endpunkt:

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)
}
Nach dem Login kopieren

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
    }
Nach dem Login kopieren

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()
    }
}
Nach dem Login kopieren

Alle anderen Endpunkte funktionieren. Wenn Sie anderen Code benötigen, lassen Sie es mich bitte wissen. Ich möchte mein Problem nicht komplizierter machen, weil ich dachte, es sei einfach, aber ich habe etwas falsch verstanden ( Vielen Dank für Ihre Hilfe und Ihren Rat.

ps.s.: Wenn ich die Middleware ausschalte, funktioniert alles wie erwartet.

Update: Validierungs- und Generierungsfunktionen hinzugefügt

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
}
Nach dem Login kopieren

Anmeldefunktion hinzugefügt, bei der ich Cookies mit JWT-String hinzugefügt habe

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})
}
Nach dem Login kopieren

Update nach Hilfe: Das Problem bestand darin, dass ich in meiner Postman-Setup-Anfrage das Cookie nicht richtig angegeben habe.

Lösung

Lassen Sie mich versuchen, Ihnen bei der Lösung des Problems zu helfen. Zunächst habe ich Ihr Beispiel etwas vereinfacht, um mich nur auf die relevanten Teile zu konzentrieren. Wenn Sie etwas auslassen müssen, lassen Sie es mich bitte wissen und ich werde die Antwort aktualisieren. Lassen Sie mich zunächst mit der lokalen auth 包中进行的 jwt Token-Generierung/-Validierung beginnen.

auth/auth.go Dateien

package 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
}
Nach dem Login kopieren

Jetzt kommen wir zum Middleware-Teil.

middlewares/middlewares.go Dateien

package 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()
    }
}
Nach dem Login kopieren

Um etwas im Kontext hochladen zu können, sollten Sie die c.set(key, value)-Methode verwenden. Kommen wir nun zu den Handlern.

handlers/handlers.go Dateien

package 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
}
Nach dem Login kopieren

Fehlende Teile wie handler.service.loginuser() 方法。要正确地从上下文中读取内容,您必须使用 c.mustget(key) Methoden werden übersprungen, da ich nicht weiß, was sie tun.

main.go Dateien

package 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")
}
Nach dem Login kopieren

Dies ist die Setup-Logik. Hier gibt es nichts Erwähnenswertes.
Wenn Sie weitere Hilfe benötigen, lassen Sie es mich bitte wissen, vielen Dank!

Das obige ist der detaillierte Inhalt vonGolang Websocket (Gorilla) mit Cookie-Authentifizierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:stackoverflow.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!