英文:
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
, asessionpackage
and apackage main
. Packages should fulfil a 'theme' - anauth
package might make more sense, or even just a singlepackage 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("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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论