草庐IT

session - Gorilla session 不会在 Golang 中持续存在

coder 2024-07-14 原文

我在使用 Gorilla session 处理程序在 Golang 中持久保存 session 时遇到了问题。类似的问题(未解决!)已在其他堆栈溢出问题中提出(此处:Sessions variables in golang not saved while using gorilla sessions 和此处:cannot get gorilla session value by key)。这非常可怕,因为看起来 1) 不仅仅是我 2) 目前似乎没有解决方案 3) Gorilla Sessions 包可能从根本上被破坏了。

这里是更详细的问题:

我可以在登录时设置 session 没有问题。但是,在我登录并向后端发出另一个请求后, session 值不会持久化,这意味着我无法提取 session 。例如,Value ['username'] (即使那是 session 的重点)。

所以:

A) 登录、写入 session 、检索 session.Value['usernames'] 并且一切正常。

B) 导航到前端的另一个页面并向后端发出另一个请求(创建一个新角色)。

C) 尝试检索 session.Value['username']。是零!!!!!!

这是用户在后端导航以登录的流程 -

首先是 session 处理程序:

package config

import (
    "log"
    "net/http"

    "github.com/gorilla/sessions"
)

type Options struct {
    Path     string
    Domain   string
    MaxAge   int
    Secure   bool
    HttpOnly bool
}

type Session struct {
    ID      string
    Values  map[interface{}]interface{}
    Options *Options
    IsNew   bool
}

type Store interface {
    Get(r *http.Request, name string) (*sessions.Session, error)
    New(r *http.Request, name string) (*sessions.Session, error)
    Save(r *http.Request, w http.ResponseWriter, s *sessions.Session) error
}

var SessionsStore = sessions.NewCookieStore([]byte("secret"))

func init() {
    SessionsStore.Options = &sessions.Options{
        Domain:   "localhost",
        Path:     "/",
        MaxAge:   3600 * 8, // 8 hours
        HttpOnly: true,
    }
}

func KeyStore() (store Store) {

    log.Print("inside KeyStore")
    store = SessionsStore
    log.Print("Value of store is : ", store)
    return store
}

接下来,这是我如何从 main 到路由到我的每个组件:

主要

package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"
    "os"

    _ "github.com/lib/pq"
    "github.com/patientplatypus/gorest/config"

    "github.com/gorilla/handlers"
)

const (
    host     = "localhost"
    port     = 5432
    user     = "patientplatypus"
    password = "superdupersecretyo"
    dbname   = "dungeon_world"
)

func main() {

    psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
        "password=%s dbname=%s sslmode=disable",
        "localhost", 5432, "patientplatypus", "supersecret", "dungeon_world")
    var err error
    config.DB, err = sql.Open("postgres", psqlInfo)
    if err != nil {
        panic(err)
    }

    err = config.DB.Ping()
    if err != nil {
        panic(err)
    }

    fmt.Println("Successfully connected~!")

    router := NewRouter()
    os.Setenv("ORIGIN_ALLOWED", "*")
    headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
    originsOk := handlers.AllowedOrigins([]string{os.Getenv("ORIGIN_ALLOWED")})
    methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

    log.Fatal(http.ListenAndServe(":8080", handlers.CORS(originsOk, headersOk, methodsOk)(router)))

}

这里是路由到每个处理程序的路由包:

package main

import (
    "net/http"

    "github.com/patientplatypus/gorest/users"

    "github.com/patientplatypus/gorest/dungeon_db"

    "github.com/patientplatypus/gorest/character"

    "github.com/patientplatypus/gorest/createcharacter"

    "github.com/gorilla/mux"
)

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

type Routes []Route

func NewRouter() *mux.Router {

    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(route.HandlerFunc)
    }

    return router
}

var routes = Routes{
    Route{
        "ClassType",
        "POST",
        "/character/class",
        character.ClassType,
    },
    <MORE ROUTES FOLLOWING SAME PATTERN>
}

现在这里是登录功能。这是我编写原始 session 并打印出 session.Values['username'] 以表明它有效的地方:

package users

import (
    "encoding/json"
    "log"
    "net/http"

    "github.com/patientplatypus/gorest/config"
)

type LoginResponse struct {
    Status string
}

type User struct {
    Username string
    Password string
    Id       int
}

func UserLogin(w http.ResponseWriter, r *http.Request) {

    decoder := json.NewDecoder(r.Body)

    var incomingjson User
    err := decoder.Decode(&incomingjson)

    if err != nil {
        panic(err)
    }

    username := incomingjson.Username
    password := incomingjson.Password

    log.Print("username: ", username)
    log.Print("password: ", password)
    if username != "" && password != "" {
        incomingjson.Login(w, r)
    } else {
        fmt.Fprintln(w, "error username or password is blank!")
    }
}

func (incomingjson *User) Login(w http.ResponseWriter, r *http.Request) {
    session, _ := config.KeyStore().Get(r, "cookie-name")
    log.Print("loginjson: ", incomingjson)
    var tempvar string

    err := config.DB.QueryRow("SELECT username FROM users WHERE username=$1;", incomingjson.Username).Scan(&tempvar)
    log.Print("err: ", err)
    if err == nil {
        // 1 row
        log.Print("Found username")
        var passwordindatabase string
        config.DB.QueryRow("SELECT password FROM users WHERE username=$1;", &incomingjson.Username).Scan(&passwordindatabase)
        if passwordindatabase == incomingjson.Password {
            log.Print("username and password match!")
            session.Values["authenticated"] = true
            session.Values["username"] = incomingjson.Username
            config.KeyStore().Save(r, w, session)
            response := LoginResponse{Status: "Success, user logged in"}
            json.NewEncoder(w).Encode(response)
        } else {
            log.Print("username and password don't match!")
            session.Values["authenticated"] = false
            session.Values["username"] = ""
            config.KeyStore().Save(r, w, session)
            response := LoginResponse{Status: "Failure, username and password don't match"}
            json.NewEncoder(w).Encode(response)
        }
    } else {
        //empty result or error
        log.Print("Username not found or there was an error: ", err)
        response := LoginResponse{Status: "User not found!"}
        json.NewEncoder(w).Encode(response)
    }
}

现在是问题组件。它的工作是在检查用户是否存在后创建一个新角色(sessioncheck 可以)...

所以这里我有:

package createcharacter

import (
    "encoding/json"
    "log"
    "net/http"

    "github.com/patientplatypus/gorest/config"
)

var Username string
var Checkok bool


func SessionsCheck(w http.ResponseWriter, r *http.Request) (username string, checkok bool) {
    store := config.KeyStore()
    session, _ := store.Get(r, "cookie-name")
    log.Print("inside sessionscheck...what is the value of stuff....")
    log.Print("session: ", session)
    log.Print("session.Values: ", session.Values)
    log.Print("username: ", session.Values["username"])
    log.Print("authenticated: ", session.Values["authenticated"])
    if session.Values["username"] == nil {
        if session.Values["authenticated"] == false {
            log.Print("Verboten!")
            http.Error(w, "Forbidden", http.StatusForbidden)
            return "nil", false
        }
    }
    return session.Values["username"].(string), true
}

func NewCharacter(w http.ResponseWriter, r *http.Request) {
    Username, Checkok = SessionsCheck(w, r)
    <FUNCTION CONTINUES>

这是我收到错误的地方...但我不知道如何修复。

终端输出为:

2017/10/15 15:08:56 inside KeyStore
2017/10/15 15:08:56 Value of store is : &{[0xc42010c000] 0xc42007d5f0}
2017/10/15 15:08:56 inside sessionscheck...what is the value of stuff....
2017/10/15 15:08:56 session: &{ map[] 0xc4201316b0 true 0xc4200e0a80 cookie-name}
2017/10/15 15:08:56 session.Values: map[]
2017/10/15 15:08:56 username: <nil>
2017/10/15 15:08:56 authenticated: <nil>
2017/10/15 15:08:56 http: panic serving [::1]:53668: interface conversion: interface {} is nil, not string
goroutine 13 [running]:
net/http.(*conn).serve.func1(0xc42015c5a0)
    /usr/local/opt/go/libexec/src/net/http/server.go:1697 +0xd0
panic(0x133bcc0, 0xc420061f00)
    /usr/local/opt/go/libexec/src/runtime/panic.go:491 +0x283
github.com/patientplatypus/gorest/createcharacter.SessionsCheck(0x1540d00, 0xc42010a540, 0xc42014ea00, 0xc42011ab00, 0x3, 0xc420001680)
    /Users/patientplatypus/Documents/golang/src/github.com/patientplatypus/gorest/createcharacter/charactercontroller.go:31 +0x5c9
github.com/patientplatypus/gorest/createcharacter.NewCharacter(0x1540d00, 0xc42010a540, 0xc42014ea00)
    /Users/patientplatypus/Documents/golang/src/github.com/patientplatypus/gorest/createcharacter/charactercontroller.go:35 +0x5a
net/http.HandlerFunc.ServeHTTP(0x13b8690, 0x1540d00, 0xc42010a540, 0xc42014ea00)
    /usr/local/opt/go/libexec/src/net/http/server.go:1918 +0x44
github.com/gorilla/mux.(*Router).ServeHTTP(0xc420066360, 0x1540d00, 0xc42010a540, 0xc42014ea00)
    /Users/patientplatypus/Documents/golang/src/github.com/gorilla/mux/mux.go:133 +0xed
github.com/gorilla/handlers.(*cors).ServeHTTP(0xc42010c7e0, 0x1540d00, 0xc42010a540, 0xc42014e800)
    /Users/patientplatypus/Documents/golang/src/github.com/gorilla/handlers/cors.go:118 +0x5c8
net/http.serverHandler.ServeHTTP(0xc42014a000, 0x1540d00, 0xc42010a540, 0xc42014e800)
    /usr/local/opt/go/libexec/src/net/http/server.go:2619 +0xb4
net/http.(*conn).serve(0xc42015c5a0, 0x1541240, 0xc420061dc0)
    /usr/local/opt/go/libexec/src/net/http/server.go:1801 +0x71d
created by net/http.(*Server).Serve
    /usr/local/opt/go/libexec/src/net/http/server.go:2720 +0x288

很抱歉冗长,但我认为这是我可以从我的代码库中重现的最简单的示例。如果有人有任何建议,请告诉我。

编辑:

我注意到的一件事如下:

2017/10/15 15:08:56 session: &{ map[] 0xc4201316b0 true 0xc4200e0a80 cookie-name}
2017/10/15 15:08:56 session.Values: map[]

似乎表明用户名和经过身份验证的 (true 0xc4200e0a80) 存储在 session.Values []map 函数的外部。这是为什么?

编辑编辑:

所以...我认为我编写 config.KeyStore() 的方式可能有问题,所以我将其重写为以下内容并在整个项目中坚持使用:

package config

import (
    "github.com/gorilla/sessions"
)

var SessionsStore = sessions.NewCookieStore([]byte("secret"))

func init() {
    SessionsStore.Options = &sessions.Options{
        Domain:   "localhost",
        Path:     "/",
        MaxAge:   3600 * 8, // 8 hours
        HttpOnly: true,
    }
}

所以现在只要我需要 SessionsStore,我就调用 conf.SessionsStore。这似乎是我认为可行的规范方法。我仍然有同样的问题。

最佳答案

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/gorilla/mux"
    "github.com/gorilla/sessions"
)

const appCookie = "myappcookies"

var cookies *sessions.CookieStore

func Login(w http.ResponseWriter, r *http.Request) {
    //
    // For the sake of simplicity, I am using a global here. 
    // You should be using a context.Context instead!
    session, err := cookies.Get(r, appCookie)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Println(err)
        return
    }
    session.Values["userName"] = "StackOverflow"
    session.Save(r, w)
}

func Session(w http.ResponseWriter, r *http.Request) {
    session, err := cookies.Get(r, appCookie)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Println(err)
        return
    }
    w.Write([]byte(fmt.Sprintf("Objects in session: %d\n", len(session.Values))))
    for k, v := range session.Values {
        w.Write([]byte(fmt.Sprintf("Key=%v, Value=%v\n", k, v)))
    }
}

func main() {
    cookies = sessions.NewCookieStore([]byte("mysuperdupersecret"))
    router := mux.NewRouter()
    router.Path("/login").Methods(http.MethodPost).HandlerFunc(Login)
    router.Path("/session").Methods(http.MethodGet).HandlerFunc(Session)
    server := &http.Server{
        Handler: router,
        Addr:    ":8000",
        // Good practice: enforce timeouts for servers you create!
        WriteTimeout: 15 * time.Second,
        ReadTimeout:  15 * time.Second,
    }
    log.Fatal(server.ListenAndServe())
}

关于session - Gorilla session 不会在 Golang 中持续存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46759540/

有关session - Gorilla session 不会在 Golang 中持续存在的更多相关文章

  1. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  2. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  3. ruby-on-rails - rspec - 如何检查方法是否存在? - 2

    我的模型有defself.empty_building//stuffend我怎样才能对这个现有的进行rspec?,已经尝试过:describe"empty_building"dosubject{Building.new}it{shouldrespond_to:empty_building}endbutgetting:Failure/Error:it{shouldrespond_to:empty_building}expected#torespondto:empty_building 最佳答案 你有一个类方法self.empty_bu

  4. jenkins部署1--jenkins+gitee持续集成 - 2

    前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon

  5. ruby-on-rails - Rails 优雅地处理超时 session ? - 2

    使用rails4,ruby2。我在rails配置中为我的cookiesession设置了30分钟的超时时间。问题是,如果我转到表单,让session超时,然后提交表单,我会收到此ActionController::InvalidAuthenticityToken错误。如何在Rails中优雅地处理这个错误?比如说,重定向到登录屏幕? 最佳答案 在您的ApplicationController:rescue_fromActionController::InvalidAuthenticityTokendoredirect_tosome_p

  6. ruby-on-rails - ActiveRecord 的 find_or_create* 方法是否存在根本性缺陷? - 2

    有几种方法:first_or_create_by、find_or_create_by等,它们的工作原理是:与数据库对话以尝试找到我们想要的东西如果我们找不到,就自己做保存到数据库显然,并发调用这些方法可能会使两个线程都找不到它们想要的东西,并且在第3步中一个线程会意外失败。似乎更好的解决方案是,创建或查找即:提前在您的数据库中创建合理的唯一性约束。如果你想保存一些东西,就保存它如果有效,那就太好了。如果它因为RecordNotUnique异常而无法工作,它已经存在,太好了,加载它那么在什么情况下我想使用Rails内置的东西而不是我自己的(看起来更可靠)create_or_find?

  7. ruby-on-rails - 为什么在 Rails 5.1.1 中删除了 session 存储初始化程序 - 2

    我去了这个website查看Rails5.0.0和Rails5.1.1之间的区别为什么5.1.1不再包含:config/initializers/session_store.rb?谢谢 最佳答案 这是删除它的提交:Setupdefaultsessionstoreinternally,nolongerthroughanapplicationinitializer总而言之,新应用没有该初始化器,session存储默认设置为cookie存储。即与在该初始值设定项的生成版本中指定的值相同。 关于

  8. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  9. ruby-on-rails - prawnto 显示新页面时不会中断的表格 - 2

    我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.

  10. ruby - Sinatra session 未按预期持续 - 2

    我正在尝试使用Sinatra中的重定向和session在网站周围传递一些数据。这是一个简化的示例,使用PrettyPrint进行调试:require'pp'require'rubygems'require'sinatra'enable:sessionsget'/'dosession[:foo]='12345'puts'session1'ppsessionredirectto('/redir')endget'/redir'doputs'session2'ppsession'helloworld'end查看Thin的输出,我看到:>>Listeningon0.0.0.0:4567,CTRL

随机推荐