首頁  >  文章  >  後端開發  >  帶有 cookie 驗證的 Golang Websocket (Gorilla)

帶有 cookie 驗證的 Golang Websocket (Gorilla)

WBOY
WBOY轉載
2024-02-11 18:03:081262瀏覽

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

在網路開發中,身份驗證是一個必不可少的功能,而基於cookie的身份驗證是一種常見的方式。 Golang作為一種高效率、簡潔的程式語言,擁有強大的Web開發能力。本文將介紹如何使用Gorilla工具包在Golang中實現帶有cookie身份驗證的Websocket功能,讓你的應用程式更加安全可靠。無論你是Golang初學者還是有一定經驗的開發者,這篇文章都能幫助你快速上手。讓我們一起來看看吧!

問題內容

我正在嘗試使用 gorilla websocket 來啟動圖表。 身份驗證中間件透過 cookie 和 jwt 令牌工作。 我所有通過 http 的端點都可以工作,但 websocket 卻不能。 在閱讀了很多像是帶有 cookie 身份驗證的 gorilla websocket 之類的主題後,我發現我的 cookie 是空的,並且 websocket 連接中的上下文也是空的。我不明白為什麼?誰能解釋為什麼? p.s.:我嘗試從該處理程序中刪除升級程序,並且 cookie 和上下文順利通過,但是在升級到 websocket 協議的連接後,它失敗了。 這是我的文件: 端點:

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
    }

中介軟體:

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()
    }
}

所有其他端點都有效,如果您需要任何其他程式碼,請告訴我。我不想讓我的問題變得更複雜,因為我認為這很簡單,但我誤解了一些東西( 感謝您的幫助和建議。

p.s.:如果我關閉中間件,一切都會如預期運作。

更新: 新增了驗證和生成函數

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
}

新增了登入功能,其中我新增了帶有 jwt 字串的 cookie

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})
}

幫助後更新: 問題出在我的 postman 設定請求中,我沒有正確指定 cookie。

解決方法

讓我試著幫助找出問題所在。首先,我稍微簡化了您的範例,以便僅專注於相關部分。如果您需要省略某些內容,請告訴我,我會更新答案。首先,讓我從本地 auth 套件中進行的 jwt 令牌產生/驗證開始。

auth/auth.go 檔案

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
}

現在,讓我們轉到中間件部分。

middlewares/middlewares.go 檔案

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()
    }
}

為了能夠在上下文中上傳某些內容,您應該使用 c.set(key, value) 方法。現在,讓我們轉向處理程序。

handlers/handlers.go 檔案

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
}

由於我不知道它們的作用,因此跳過了缺少的部分,例如 handler.service.loginuser() 方法。要正確地從上下文中讀取內容,您必須使用 c.mustget(key) 方法。

main.go 檔案

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")
}

這是設定邏輯。這裡沒什麼好提的。
如果您還需要其他協助,請告訴我,謝謝!

以上是帶有 cookie 驗證的 Golang Websocket (Gorilla)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除