英文:
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 (
"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)
}
}
<h3>LoginPkg.go</h3>
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()
}
The problem lies in following file....
<h3>sessionHandler.go</h3>
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)
}
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: "session", // This should be encoded. Value: sessionData["userName"], //Here i want map or something else that can accept multiple values Expires: expiration, //MaxAge: 3600, } -
Too many packages: you have a
loginpackage, asessionpackageand apackage main. Packages should fulfil a 'theme' - anauthpackage might make more sense, or even just a singlepackage mainuntil 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("password")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 (
"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)
}
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, "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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论