如何使用map在cookie中传递多个值

huangapple go评论142阅读模式
英文:

How to pass multiple values in cookie with map

问题

在使用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) // 设置监听端口
	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) // 将map传递给函数,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()
}

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

<h3>sessionHandler.go</h3>

```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"], // 这里我想要一个可以接受多个值的map或其他类型
		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()。它没有解码传递给它的数据。

英文:

Got stuck while passing multiple values in cookie. I'm not able to find a better way for managing session and cookies. Trying to use github.com/gorilla/securecookie this package.
<h3>loginMain.go</h3>
package main

import (
&quot;database/sql&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;shambhavi/packages/loginPkg&quot;   
_ &quot;github.com/go-sql-driver/mysql&quot;
&quot;github.com/gorilla/mux&quot;
)
var router = mux.NewRouter()
var db *sql.DB
func connectDb() *sql.DB {
db, dberr := sql.Open(&quot;mysql&quot;, &quot;root:root@tcp(127.0.0.1:8889)/shambhavi_db&quot;)
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(&quot;/demo&quot;, login)
http.Handle(&quot;/&quot;, router)
err := http.ListenAndServe(port, nil) // setting listening port
if err != nil {
log.Fatal(&quot;ListenAndServe: &quot;, err)
}
}  

<h3>LoginPkg.go</h3>

package loginPkg
import (
&quot;database/sql&quot;
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;net/http&quot;
&quot;shambhavi/packages/sessionPkg&quot;
_ &quot;github.com/go-sql-driver/mysql&quot;
)
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(&quot;In LoginOperation &quot;)
r.ParseForm()
if len(r.Form[&quot;username&quot;][0]) == 0 &amp;&amp; len(r.Form[&quot;password&quot;][0]) == 0 {
fmt.Fprintf(w, &quot;Something is blank !!!&quot;)
} else {
var lvl string
var uFullName string
err := db.QueryRow(&quot;SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?&quot;, r.FormValue(&quot;username&quot;), r.FormValue(&quot;password&quot;)).Scan(&amp;lvl)
er := db.QueryRow(&quot;SELECT emp_name FROM emp_detail WHERE emp_uname = ?&quot;, r.FormValue(&quot;username&quot;)).Scan(&amp;uFullName)
ErrorHandler(er)
retMap[&quot;msg&quot;] = &quot;Login successfully&quot;
retMap[&quot;err&quot;] = &quot;Not Login&quot;
retMap[&quot;lvl&quot;] = lvl
retMap[&quot;fullName&quot;] = uFullName
b, _ := json.Marshal(retMap)
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, &quot;%s&quot;, b)
} else {
if lvl == &quot;1&quot; || lvl == &quot;2&quot; || lvl == &quot;3&quot; {
sessionPkg.SetSession(w, r, r.FormValue(&quot;username&quot;), retMap) // Passing map to the fun, retMap
fmt.Fprintf(w, &quot;%s&quot;, b)
usrnm := sessionPkg.GetUserName(r)
fmt.Println(&quot;From session variable&quot;, usrnm)
} else {
fmt.Println(&quot;Chukala ....&quot;)
fmt.Fprintf(w, &quot;%s&quot;, b)
}	
}
}
defer db.Close()
}

The problem lies in following file....
<h3>sessionHandler.go</h3>

package sessionPkg
import (
&quot;fmt&quot;
&quot;net/http&quot;
&quot;time&quot;
&quot;github.com/gorilla/securecookie&quot;
)
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{
&quot;userName&quot;: username,
&quot;lvl&quot;:      retMap[&quot;lvl&quot;],
&quot;fullName&quot;: retMap[&quot;fullName&quot;],
}
expiration := time.Now().Add(365 * 24 * time.Hour)
//if encoded, err := cookieHandler.Encode(&quot;session&quot;, sessionData); err == nil {
cookie := http.Cookie{
Name:    &quot;session&quot;,
Value:   sessionData[&quot;userName&quot;], //Here i want map or something else that can accept multiple values
Expires: expiration,
//MaxAge: 3600,
}
http.SetCookie(w, &amp;cookie)
//}
}
func GetUserName(request *http.Request) (userName string) {
//fmt.Println(request.Cookie(&quot;session&quot;))
cookieValue := make(map[string]string)
if cookie, err := request.Cookie(&quot;session&quot;); err == nil {
fmt.Println(&quot;cookieValue = &quot;, cookie.Value)
//if err = cookieHandler.Decode(&quot;session&quot;, cookie.Value, &amp;cookieValue); err == nil {
//fmt.Println(cookie)
cookieValue[&quot;userName&quot;] = cookie.Value
//fmt.Println(cookieValue[&quot;userName&quot;])
//}
/*else {
fmt.Println(&quot;Error &quot;, err)
}*/
}
return cookieValue[&quot;userName&quot;]
}
/*func GetFullName(request *http.Request) (fullName string) {
fmt.Println(&quot;In GetFullName&quot;)
cookieValue := make(map[string]string)
if cookie2, err := request.Cookie(&quot;session&quot;); err == nil {
fmt.Println(&quot;cookieValue = &quot;, cookie2.Value)
//if err = cookieHandler.Decode(&quot;session&quot;, cookie.Value, &amp;cookieValue); err == nil {
fmt.Println(cookie2)
cookieValue[&quot;fullName&quot;] = cookie2.Value
fmt.Println(cookieValue[&quot;fullName&quot;])
//}
}
return cookieValue[&quot;fullName&quot;]
}*/
func ClearSession(response http.ResponseWriter) {
cookie := &amp;http.Cookie{
Name:   &quot;session&quot;,
Value:  &quot;&quot;,
Path:   &quot;/&quot;,
MaxAge: -1,
}
http.SetCookie(response, cookie)
}

Problem indicated in code by comment. I want to use session like in PHP. Suggest the better way to secure the cookie and maintain the session. Give some explanation too.

Edited: Explain cookieHandler.Encode() and cookieHandler.Decode(). It is not decoding the data which is passed to it.

答案1

得分: 4

  • var retMap = make(map[string]string) 是一个全局的映射,你既可以读取也可以写入它,这是不安全的:当你有多个用户并发时,会覆盖该映射的内容。

  • 你没有使用 securecookie 包来对 cookie 值进行编码 - 实际上不清楚你在哪里使用它。

      cookie := http.Cookie{
    Name:    "session",
    // 这里应该进行编码。
    Value:   sessionData["userName"], // 这里我想要一个可以接受多个值的映射或其他类型
    Expires: expiration,
    //MaxAge: 3600,
    }
    
  • 包太多了:你有一个 loginpackage,一个 sessionpackage 和一个 package main。包应该具有一个“主题” - 一个 auth 包可能更合适,或者甚至只有一个 package main,直到你对 Go 更熟悉为止。

  • 你没有对密码进行哈希处理 - 在数据库中存储明文密码,并使用 r.FormValue("password") 的值进行查找是极其不安全的。阅读这篇文章以了解如何在 Go 中安全地哈希密码:https://stackoverflow.com/questions/18545676/golang-app-engine-securely-hashing-a-users-password

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

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

package main
import (
"net/http"
"github.com/gorilla/sessions"
)
// 使用 CookieStore
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func LoginOperation(w http.ResponseWriter, r *http.Request) {
// 你现有的数据库代码 - 这里缩短以使示例更清晰
err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)
// 不要使用全局映射 - 创建一个新的
userDetails := make(map[string]string)
userDetails["msg"] = "登录成功"
userDetails["err"] = "未登录"
userDetails["lvl"] = lvl
userDetails["fullName"] = uFullName
// 获取一个会话(现有/新建)
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// 设置一些会话值。
session.Values["userDetails"] = userDetails
// 在写入响应/从处理程序返回之前保存会话。
session.Save(r, w)
}

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

func RequireAuth(w http.ResponseWriter, r *http.Request) {
// 获取一个会话(现有/新建)
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// 类型断言我们的 userDetails 映射从会话的 map[string]interface{} 中取出
userDetails, ok := session.Values["userDetails"].(map[string]string)
if !ok {
// 用户没有现有会话 - 将其视为未登录和/或重定向到登录页面。
http.Error(w, http.StatusCode(401), 401)
return
}
// 检查用户详细信息 - 例如,如果 userDetails["lvl"] == "ACTIVE" { ... }
// TODO
}
英文:
  • var retMap = make(map[string]string) is a global map that you both read and write to, which is unsafe: when you have more than one user concurrently you will be overwriting the contents of this map.

  • You aren't using the securecookie package to encode your cookie values at all - in fact it's not clear where you're using it at all.

      cookie := http.Cookie{
    Name:    &quot;session&quot;,
    // This should be encoded.
    Value:   sessionData[&quot;userName&quot;], //Here i want map or something else that can accept multiple values
    Expires: expiration,
    //MaxAge: 3600,
    }
    
  • Too many packages: you have a loginpackage, a sessionpackage and a package main. Packages should fulfil a 'theme' - an auth package might make more sense, or even just a single package main until you get more familiar with Go.

  • You're not hashing your passwords - storing the plain text password in the database and looking it up with the value of r.FormValue(&quot;password&quot;) is extremely insecure. Read this to see how to hash passwords safely in Go: https://stackoverflow.com/questions/18545676/golang-app-engine-securely-hashing-a-users-password

  • You should be using the gorilla/sessions package rather than the lower-level securecookie package.

Modifying the gorilla/sessions example in the documentation:

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

Later, if you want to retrieve the details:

func RequireAuth(w http.ResponseWriter, r *http.Request) {
// Get a session (existing/new)
session, err := store.Get(r, &quot;session-name&quot;)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Type assert our userDetails map out of the session&#39;s map[string]interface{}
userDetails, ok := session.Values[&quot;userDetails&quot;].(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[&quot;lvl&quot;] == &quot;ACTIVE&quot; { ... }
// TODO
}

huangapple
  • 本文由 发表于 2015年10月26日 14:53:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/33339986.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定