Session 使用gorilla会话时未保存golang中的会话变量

Session 使用gorilla会话时未保存golang中的会话变量,session,go,gorilla,Session,Go,Gorilla,在使用gorilla sessions web toolkit时,不会跨请求维护会话变量。 当我启动服务器并键入localhost:8100/时,页面被定向到login.html,因为会话值不存在。登录后,我在存储中设置会话变量,页面被重定向到home.html。但是,当我打开一个新选项卡并键入localhost:8100/时,应该使用已经存储的会话变量将页面定向到home.html,但是页面被重定向到login.html。 下面是代码 package main import (

在使用gorilla sessions web toolkit时,不会跨请求维护会话变量。 当我启动服务器并键入localhost:8100/时,页面被定向到login.html,因为会话值不存在。登录后,我在存储中设置会话变量,页面被重定向到home.html。但是,当我打开一个新选项卡并键入localhost:8100/时,应该使用已经存储的会话变量将页面定向到home.html,但是页面被重定向到login.html。 下面是代码

    package main

import (
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "github.com/gocql/gocql"
    "github.com/gorilla/mux"
    "github.com/gorilla/sessions"
    "net/http"
    "time"
)

var store = sessions.NewCookieStore([]byte("something-very-secret"))

var router = mux.NewRouter()

func init() {

    store.Options = &sessions.Options{
        Domain:   "localhost",
        Path:     "/",
        MaxAge:   3600 * 1, // 1 hour
        HttpOnly: true,
    }
}
func main() {
    //session handling
    router.HandleFunc("/", SessionHandler)
    router.HandleFunc("/signIn", SignInHandler)
    router.HandleFunc("/signUp", SignUpHandler)
    router.HandleFunc("/logOut", LogOutHandler)
    http.Handle("/", router)
    http.ListenAndServe(":8100", nil)
}

//handler for signIn
func SignInHandler(res http.ResponseWriter, req *http.Request) {

    email := req.FormValue("email")
    password := req.FormValue("password")

    //Generate hash of password
    hasher := md5.New()
    hasher.Write([]byte(password))
    encrypted_password := hex.EncodeToString(hasher.Sum(nil))

    //cassandra connection
    cluster := gocql.NewCluster("localhost")
    cluster.Keyspace = "gbuy"
    cluster.DefaultPort = 9042
    cluster.Consistency = gocql.Quorum
    session, _ := cluster.CreateSession()
    defer session.Close()

    //select query
    var firstname string
    stmt := "SELECT firstname FROM USER WHERE email= '" + email + "' and password ='" + encrypted_password + "';"
    err := session.Query(stmt).Scan(&firstname)
    if err != nil {
        fmt.Fprintf(res, "failed")
    } else {
        if firstname == "" {
            fmt.Fprintf(res, "failed")
        } else {
            fmt.Fprintf(res, firstname)
        }
    }

    //store in session variable
    sessionNew, _ := store.Get(req, "loginSession")

    // Set some session values.
    sessionNew.Values["email"] = email
    sessionNew.Values["name"] = firstname

    // Save it.
    sessionNew.Save(req, res)
    //store.Save(req,res,sessionNew)

    fmt.Println("Session after logging:")
    fmt.Println(sessionNew)

}

//handler for signUp
func SignUpHandler(res http.ResponseWriter, req *http.Request) {

    fName := req.FormValue("fName")
    lName := req.FormValue("lName")
    email := req.FormValue("email")
    password := req.FormValue("passwd")
    birthdate := req.FormValue("date")
    city := req.FormValue("city")
    gender := req.FormValue("gender")

    //Get current timestamp and format it.
    sysdate := time.Now().Format("2006-01-02 15:04:05-0700")

    //Generate hash of password
    hasher := md5.New()
    hasher.Write([]byte(password))
    encrypted_password := hex.EncodeToString(hasher.Sum(nil))

    //cassandra connection
    cluster := gocql.NewCluster("localhost")
    cluster.Keyspace = "gbuy"
    cluster.DefaultPort = 9042
    cluster.Consistency = gocql.Quorum
    session, _ := cluster.CreateSession()
    defer session.Close()

    //Insert the data into the Table
    stmt := "INSERT INTO USER (email,firstname,lastname,birthdate,city,gender,password,creation_date) VALUES ('" + email + "','" + fName + "','" + lName + "','" + birthdate + "','" + city + "','" + gender + "','" + encrypted_password + "','" + sysdate + "');"
    fmt.Println(stmt)
    err := session.Query(stmt).Exec()
    if err != nil {
        fmt.Fprintf(res, "failed")
    } else {
        fmt.Fprintf(res, fName)
    }
}

//handler for logOut
func LogOutHandler(res http.ResponseWriter, req *http.Request) {
    sessionOld, err := store.Get(req, "loginSession")

    fmt.Println("Session in logout")
    fmt.Println(sessionOld)
    if err = sessionOld.Save(req, res); err != nil {
        fmt.Println("Error saving session: %v", err)
    }
}

//handler for Session
func SessionHandler(res http.ResponseWriter, req *http.Request) {

    router.PathPrefix("/").Handler(http.FileServer(http.Dir("../static/")))
    session, _ := store.Get(req, "loginSession")

    fmt.Println("Session in SessionHandler")
    fmt.Println(session)


    if val, ok := session.Values["email"].(string); ok {
        // if val is a string
        switch val {
        case "": {
            http.Redirect(res, req, "html/login.html", http.StatusFound) }
        default:
            http.Redirect(res, req, "html/home.html", http.StatusFound)
        }
    } else {
        // if val is not a string type
        http.Redirect(res, req, "html/login.html", http.StatusFound)
    }
}

谁能告诉我我做错了什么。提前感谢。

首先:永远不要使用md5散列密码。为什么,然后用围棋。您还应该知道,您可能会受到灾难性的SQL注入攻击

无论如何:这里有几个问题需要解决:

  • 您的会话没有“粘滞”是因为您将
    路径设置为
    /loginSession
    ——因此当用户访问任何其他路径(即
    /
    )时,会话对该范围无效
您应该在程序初始化时设置会话存储,并在其中设置选项:

var store = sessions.NewCookieStore([]byte("something-very-secret"))

func init() {

   store.Options = &sessions.Options{
    Domain:   "localhost",
    Path:     "/",
    MaxAge:   3600 * 8, // 8 hours
    HttpOnly: true,
}
您可能会设置更具体的路径的原因是,如果登录的用户总是在子路由中,如
/accounts
。在你的情况下,事情不是这样的

我应该补充一点,Chrome在Web Inspector(资源>Cookies)中的“资源”选项卡对于调试类似问题非常有用,因为您可以看到cookie过期、路径和其他设置

  • 您还检查了session.Values[“email”]==nil,这不起作用。Go中的空字符串只是
    ,因为
    session.Values
    是一个
    map[string]接口{}
    ,所以需要将值转换为字符串:
i、 e

我们处理“nota string”的情况,因此我们明确了如果会话不是我们期望的那样,程序应该做什么(客户端修改了它,或者我们的程序的旧版本使用了不同的类型)

  • 保存会话时,您没有检查错误

    sessionNew.Save(req, res)
    
。。。应该是:

    err := sessionNew.Save(req, res)
    if err != nil {
            // handle the error case
    }
  • 在提供静态文件之前,您应该在
    SessionHandler
    中获取/验证会话(不过,您的操作非常迂回):


在注释链之后,请尝试从会话选项中删除
约束,或将其替换为解析的FQDN(例如使用
/etc/hosts

这似乎是Chromium中的一个bug,其中不发送具有显式“localhost”域的cookie。Firefox中似乎没有出现这个问题

我可以使用

store.Options = &sessions.Options{
    // Domain: "localhost",
    MaxAge:   3600 * 1, // 1 hour
    HttpOnly: true,
}

问题是您在调用
会话.Save
之前正在写入响应。这样可以防止写入标头,从而防止将cookie发送到客户端

session.Query
之后的代码中,您正在对响应调用
Fprintf
,一旦此代码执行,调用
sessionNew.Save
基本上不起任何作用。请删除写入响应的所有代码,然后重试

如果响应已经写入,我想gorilla toolkit的会话在调用Save时应该返回一个错误。

使用服务器端的“FilesystemStore”而不是“CookieStore”来保存会话变量。另一种选择是将会话更新为请求的上下文变量,即将会话存储在上下文中,并让浏览器使用gorilla/context包中的context.Set()在每个请求中传递会话

对于客户端来说,使用“CookieStore”是一件很麻烦的事情,因为随着存储在cookie中的信息量的增加,每一个请求和响应都会通过网络传输更多的信息。它的优点是不需要在服务器端存储会话信息。如果在服务器上存储会话信息不是一种约束,那么理想的方法应该是将登录和身份验证相关的信息存储在服务器端的“非cookie”会话存储中,并将令牌传递给客户端。服务器将维护令牌和会话信息的映射。“文件系统存储”允许您这样做


尽管“FilesystemStore”和“CookieStore”都实现了“Store”接口,但它们的每个“Save()”函数的实现都略有不同。这两个函数的源代码,将帮助我们理解为什么“CookieStore”不能持久化会话信息。FilesystemStore的Save()方法除了将会话信息写入响应头之外,还将信息保存在服务器端会话文件中。在“CookieStore”实现中,如果浏览器无法将新修改的cookie从响应发送到下一个请求,则请求可能会失败。在“FilesystemStore”实现中,提供给浏览器的令牌始终保持不变。会话信息在文件中更新,并在需要时根据请求令牌获取。

在我的情况下,问题在于路径。我知道问题不在于此,但当你搜索谷歌时,这篇文章首先出现。因此,我在如下路径中启动会话:

/usuario/login
因此路径被设置为/usuario,然后,当我从
/
发出另一个请求时,cookie没有设置,因为/与/usuario不一样

我通过指定一条路径来修复它,我知道这应该是显而易见的,但我花了几个小时才意识到这一点。因此:

&sessions.Options{
        MaxAge:   60 * 60 * 24,
        HttpOnly: true,
        Path:     "/", // <-- This is very important
    }
&sessions.Options{
最大值:60*60*24,
HttpOnly:true,

路径:“/”,//我添加了init()并删除了会话。选项将路径设置为/loginSession,但它仍然不起作用。它仍然不起作用。我添加了打印语句
/usuario/login
&sessions.Options{
        MaxAge:   60 * 60 * 24,
        HttpOnly: true,
        Path:     "/", // <-- This is very important
    }