使用gorilla sessions Web工具包时,不会跨请求维护会话变量。 当我启动服务器并输入localhost时:8100 / page被定向到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)
}
}
有人可以告诉我我做错了什么。提前谢谢。
答案 0 :(得分:23)
首先:你永远不应该使用md5来哈希密码。 Read this article了解原因,然后使用Go的bcrypt package。您还应该parameterise your SQL queries否则您可以接受灾难性 SQL注入攻击。
无论如何:你需要解决一些问题:
Path
设置为/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“资源”标签(资源> Cookie)对调试这些问题非常有用,因为您可以看到Cookie过期,路径和其他设置。
session.Values["email"] == nil
,但这不起作用。 Go中的空字符串只是""
,因为session.Values
是map[string]interface{}
,您需要type assert字符串的值:即
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)
}
我们处理“非字符串”的情况,因此我们明确说明如果会话不是我们预期的方式,程序应该做什么(客户端修改它,或者我们程序的旧版本使用不同的类型)。
保存会话时,您不会检查错误。
sessionNew.Save(req, res)
......应该是:
err := sessionNew.Save(req, res)
if err != nil {
// handle the error case
}
您应该在<{1}} 提供静态文件之前获取/验证会话(但是,您是以非常迂回的方式进行):
SessionHandler
答案 1 :(得分:4)
问题是你在调用session.Save
之前写了回复。这样可以防止编写标题,从而将cookie发送给客户端。
在session.Query
之后的代码中,您在响应中调用Fprintf
,一旦执行此代码,调用sessionNew.Save
基本上什么都不做。删除任何写入响应的代码,然后重试。
如果响应已被写入,我猜gorilla toolkit的会话应该在调用Save时返回错误。
答案 2 :(得分:1)
从评论链开始,请尝试从会话选项中删除Domain
约束,或将其替换为可解析的FQDN(例如,使用/etc/hosts
)。
这似乎是Chromium中的一个错误,其中未发送带有显式“localhost”域的Cookie。 Firefox中似乎没有出现这个问题。
我能够使用
让你的演示工作store.Options = &sessions.Options{
// Domain: "localhost",
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
答案 3 :(得分:0)
使用服务器端“FilesystemStore”而不是“CookieStore”来保存会话变量。另一种选择是将会话更新为请求的上下文变量,即将会话存储在上下文中,并让浏览器在每个请求中使用来自gorilla / context包的context.Set()传递它。
使用“CookieStore”对客户来说很重要,因为随着cookie中存储的信息量的增加,每次请求和响应都会通过网络传输更多信息。它所服务的优点是不需要在服务器端存储会话信息。如果不是在服务器上存储会话信息的约束,理想的方法应该是在服务器端“非cookie”会话存储上存储登录和身份验证相关信息,并将令牌传递给客户端。服务器将维护令牌和会话信息的映射。 “FilesystemStore”允许您执行此操作。
虽然“FilesystemStore”和“CookieStore”都实现了“Store”接口,但它们的每个“Save()”函数的实现都略有不同。这两个函数CookieStore.Save()和FilesystemStore.Save()的源代码将帮助我们理解为什么“CookieStore”无法持久保存会话信息。除了将会话信息写入响应头之外,FilesystemStore的Save()方法还将信息保存在服务器端会话文件中。在“CookieStore”实现中,如果浏览器无法从对下一个请求的响应中发送新修改的cookie,则该请求可能会失败。在“FilesystemStore”实现中,给予浏览器的令牌始终保持不变。会话信息在文件中更新,并在需要时根据请求令牌获取。
答案 4 :(得分:0)
在我看来,问题出在路径。我知道问题不关乎此,但您搜索Google时,该帖子会首先出现。所以,我在这样的路径下开始会话:
tflite.run
因此路径设置为/ usuario,然后,当我从/usuario/login
发出另一个请求时,由于 /与/ usuario
我通过指定路径来修复它,我知道这应该很明显,但是花了我几个小时才意识到。所以:
/
有关常规Cookie的更多信息:https://developer.mozilla.org/es/docs/Web/HTTP/Cookies