【问题标题】:How to pass multiple values in cookie with map如何使用map在cookie中传递多个值
【发布时间】:2023-03-29 02:52:01
【问题描述】:

在 cookie 中传递多个值时卡住了。我找不到更好的方法来管理会话和 cookie。尝试使用github.com/gorilla/securecookie 这个包。

loginMain.go

package main

import (
    "database/sql"
    "log"
    "net/http"
    "shambhavi/packages/loginPkg"   
    _ "github.com/go-sql-driver/mysql"
    "github.com/gorilla/mux"
)

var router = mux.NewRouter()
var db *sql.DB

func connectDb() *sql.DB {
    db, dberr := sql.Open("mysql", "root:root@tcp(127.0.0.1:8889)/shambhavi_db")
    if dberr != nil {
        log.Println(dberr)
    }
    return db
}
func login(w http.ResponseWriter, r *http.Request) {
    var db *sql.DB = connectDb()
    loginPkg.LoginOperation(w, r, db)

}

func main() {
    http.HandleFunc("/demo", login)
    http.Handle("/", router)
    err := http.ListenAndServe(port, nil) // setting listening port
if err != nil {
    log.Fatal("ListenAndServe: ", err)
}
}  

LoginPkg.go

package loginPkg

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "net/http"
    "shambhavi/packages/sessionPkg"

    _ "github.com/go-sql-driver/mysql"
)

var retMap = make(map[string]string)

func ErrorHandler(err error) {
    if err != nil {
        panic(err)
    }
}

func LoginOperation(w http.ResponseWriter, r *http.Request, db *sql.DB) {
    fmt.Println("In LoginOperation ")
    r.ParseForm()
    if len(r.Form["username"][0]) == 0 && len(r.Form["password"][0]) == 0 {
        fmt.Fprintf(w, "Something is blank !!!")
    } else {
        var lvl string
        var uFullName string
        err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)

        er := db.QueryRow("SELECT emp_name FROM emp_detail WHERE emp_uname = ?", r.FormValue("username")).Scan(&uFullName)
        ErrorHandler(er)
        retMap["msg"] = "Login successfully"
        retMap["err"] = "Not Login"
        retMap["lvl"] = lvl
        retMap["fullName"] = uFullName
        b, _ := json.Marshal(retMap)
        if err != nil {
            fmt.Println(err)
            fmt.Fprintf(w, "%s", b)

        } else {
            if lvl == "1" || lvl == "2" || lvl == "3" {
                sessionPkg.SetSession(w, r, r.FormValue("username"), retMap) // Passing map to the fun, retMap
                fmt.Fprintf(w, "%s", b)
                usrnm := sessionPkg.GetUserName(r)
                fmt.Println("From session variable", usrnm)
            } else {
                fmt.Println("Chukala ....")
                fmt.Fprintf(w, "%s", b)
            }   
        }
    }
    defer db.Close()
}

问题出在以下文件....

sessionHandler.go

package sessionPkg

import (
    "fmt"
    "net/http"
    "time"

    "github.com/gorilla/securecookie"
)

var cookieHandler = securecookie.New(
    securecookie.GenerateRandomKey(64),
    securecookie.GenerateRandomKey(32))


func SetSession(w http.ResponseWriter, r *http.Request, username string, retMap map[string]string) {

    sessionData := map[string]string{
        "userName": username,
        "lvl":      retMap["lvl"],
        "fullName": retMap["fullName"],
    }

    expiration := time.Now().Add(365 * 24 * time.Hour)

    //if encoded, err := cookieHandler.Encode("session", sessionData); err == nil {
    cookie := http.Cookie{
        Name:    "session",
        Value:   sessionData["userName"], //Here i want map or something else that can accept multiple values
        Expires: expiration,
        //MaxAge: 3600,
    }

    http.SetCookie(w, &cookie)
    //}
}

func GetUserName(request *http.Request) (userName string) {
    //fmt.Println(request.Cookie("session"))
    cookieValue := make(map[string]string)
    if cookie, err := request.Cookie("session"); err == nil {

        fmt.Println("cookieValue = ", cookie.Value)
        //if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
        //fmt.Println(cookie)
        cookieValue["userName"] = cookie.Value
        //fmt.Println(cookieValue["userName"])
        //}
        /*else {
            fmt.Println("Error ", err)
        }*/
    }
    return cookieValue["userName"]
}

/*func GetFullName(request *http.Request) (fullName string) {
    fmt.Println("In GetFullName")
    cookieValue := make(map[string]string)
    if cookie2, err := request.Cookie("session"); err == nil {

        fmt.Println("cookieValue = ", cookie2.Value)
        //if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
        fmt.Println(cookie2)
        cookieValue["fullName"] = cookie2.Value
        fmt.Println(cookieValue["fullName"])
        //}
    }
    return cookieValue["fullName"]
}*/

func ClearSession(response http.ResponseWriter) {
    cookie := &http.Cookie{
        Name:   "session",
        Value:  "",
        Path:   "/",
        MaxAge: -1,
    }
    http.SetCookie(response, cookie)
}

代码中通过注释指出的问题。我想像在 PHP 中一样使用会话。建议更好的方法来保护 cookie 和维护会话。也给一些解释。

已编辑:解释 cookieHandler.Encode()cookieHandler.Decode()。它不解码传递给它的数据。

【问题讨论】:

    标签: go session-cookies


    【解决方案1】:
    • var retMap = make(map[string]string) 是一个您可以读写的全局映射,这是不安全的:当您同时拥有多个用户时,您将覆盖此映射的内容。

    • 您根本没有使用 securecookie 包来编码您的 cookie 值 - 实际上根本不清楚您在哪里使用它。

      cookie := http.Cookie{
          Name:    "session",
          // This should be encoded.
          Value:   sessionData["userName"], //Here i want map or something else that can accept multiple values
          Expires: expiration,
          //MaxAge: 3600,
      }
      
    • 包太多:您有一个loginpackage、一个sessionpackage 和一个package main。包应该满足一个“主题”——auth 包可能更有意义,或者甚至只是一个 package main,直到您更熟悉 Go。

    • 您没有对密码进行哈希处理 - 将纯文本密码存储在数据库中并使用 r.FormValue("password") 的值进行查找是非常不安全的。阅读本文以了解如何在 Go 中安全地散列密码:Golang/App Engine - securely hashing a user's password

    • 您应该使用gorilla/sessions 包而不是较低级别的securecookie 包。

    修改文档中的 gorilla/sessions 示例:

    package main
    
    import (
        "net/http"
        "github.com/gorilla/sessions"
    )
    
    // Use the CookieStore
    var store = sessions.NewCookieStore([]byte("something-very-secret"))
    
    func LoginOperation(w http.ResponseWriter, r *http.Request) {
        // Your existing DB code - shorter to make the example here clearer
        err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)
    
        // Don't use a global map - create a new one
        userDetails := make(map[string]string)
        userDetails["msg"] = "Login successfully"
        userDetails["err"] = "Not Login"
        userDetails["lvl"] = lvl
        userDetails["fullName"] = uFullName
    
        // Get a session (existing/new)
        session, err := store.Get(r, "session-name")
        if err != nil {
            http.Error(w, err.Error(), 500)
            return
        }
    
        // Set some session values.
        session.Values["userDetails"] = userDetails
        // Save it before we write to the response/return from the handler.
        session.Save(r, w)
    }
    

    稍后,如果您想检索详细信息:

    func RequireAuth(w http.ResponseWriter, r *http.Request) {
        // Get a session (existing/new)
        session, err := store.Get(r, "session-name")
        if err != nil {
            http.Error(w, err.Error(), 500)
            return
        }
    
        // Type assert our userDetails map out of the session's map[string]interface{}
        userDetails, ok := session.Values["userDetails"].(map[string]string)
        if !ok {
            // User does not have an existing session - treat them as not logged in and/or re-direct them to your login page.
            http.Error(w, http.StatusCode(401), 401)
            return
        }
    
    
        // Check the user details - e.g. if userDetails["lvl"] == "ACTIVE" { ... }
        // TODO
        }
    

    【讨论】:

    • 感谢 elithrar 的详细回答。为它+1。用Cookie好不好?通过编码(加密)cookie 数据。
    • 签名 cookie 不会被(坏)用户修改,gorilla/sessions 默认提供此功能。您可以选择对它们进行加密,这样可以防止它们被读取,但如果您通过 HTTPS 为您的网站提供服务(您应该这样做),那么这并不是完全必要的。
    猜你喜欢
    • 2013-11-15
    • 2014-05-14
    • 2021-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 2017-06-13
    相关资源
    最近更新 更多