The Missing Session Cookie While Using GoLang Gin

  sonic0002        2024-12-02 04:30:06       1,003        0          English  中文  Tiếng Việt 

Gin 是Go语言中广泛使用的Web框架,它提供了强大的功能来简化网站开发。在其众多功能中,处理会话和Cookie是构建功能性Web应用程序的关键方面。

这篇文章重点介绍了Gin框架中一种可能并不明显或直观的行为——丢失的会话Cookie问题

那么,丢失的会话Cookie问题究竟是什么?它发生在会话在一个路由中创建并在其中保存,但在其他路由中却无法访问时。此外,带有指定键的预期会话Cookie不存在或未发送到浏览器。以下是此问题如何表现的更详细说明。

我们基本上有两个页面:/signin/stock。/signin页面只是一个简单的登录表单,包含两个字段:用户名和密码。一旦用户输入并提交表单,请求数据将被发送到后端API,并且一旦用户名和密码被验证,将创建一个包含用户名的会话并保存。

session := sessions.Default(c)
session.Set("username", username)
session.Options(sessions.Options{MaxAge: 60 * 2}) // 测试目的,2分钟
session.Save()

登录后,用户将被重定向到/stock页面,该页面显示一些股票信息(不重要),但它需要会话数据可用(用户需要先登录),以便在访问时不会滥用API。在/stock API中,会话数据将像这样读取

func AuthMiddleware() gin.HandlerFunc {
	fmt.Println("初始化身份验证中间件")
	return func(c *gin.Context) {
		session := sessions.Default(c)
		username := session.Get("username")

		if username == nil && c.Request.URL.Path != "/signin" && !strings.HasPrefix(c.Request.URL.Path, "/api/") {
			c.Redirect(http.StatusTemporaryRedirect, "signin")
			c.Abort()
			return
		}
		c.Next()
	}
}

这是一个中间件,每次请求到来时都会运行。会话存储本身在服务器启动时初始化。

// 初始化Cookie存储
store := cookie.NewStore([]byte("secret"))

// 使用会话中间件
r.Use(sessions.Sessions("mysecurity", store))

最初所有这些看起来都相当标准并且应该可以正常工作。这段代码在网上也很常见。但是,在测试过程中,/stock页面从未被重定向,并且会话Cookie未在浏览器中正确设置,因为找不到上面指定的名称的Cookie。

为了调试,我首先确认登录过程本身没有问题。用户名和密码被后端API正确接收,并且session.Set()session.Save()函数被调用。只是Cookie没有正确设置。

问题是什么?问题实际上在于这一行

session.Options(sessions.Options{MaxAge: 60 * 2})

通过此函数调用,Cookie选项将设置为指定的选项。设置了max-age,仅此而已。HTTP Cookie的其他元素被省略,例如PATH。如果PATH为空,某些浏览器(至少Microsoft Edge)不会接受Cookie,因此不会保存它。

SetCookie: mysecurity=MTczMjcwNzQUJFQUVRQUFBcV80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQW9BQ0hCcGEyVXdNREF5fDuc3NUMqZ5fjluUzVQbDCSpiN-NaHO5BgwXkXCyi01x; Expires=Wed, 27 Nov 2024 11:41:53 GMT; Max-Age=600

根据Gin的实现,这种设置Cookie的行为是预期的,因为我已经调用了session.Options(),它基本上会覆盖整个sessions.Options,其中也包含其他字段。

type Options struct {
	Path   string
	Domain string
	// MaxAge=0 表示未指定“Max-Age”属性。
	// MaxAge<0 表示立即删除Cookie,相当于“Max-Age: 0”。
	// MaxAge>0 表示存在Max-Age属性,并以秒为单位给出。
	MaxAge   int
	Secure   bool
	HttpOnly bool
	// rfc-draft 用于防止CSRF:https://tools.ietf.org/html/draft-west-first-party-cookies-07
	//   参考:https://godoc.org/net/http
	//          https://www.sjoerdlangkemper.nl/2016/04/14/preventing-csrf-with-samesite-cookie-attribute/
	SameSite http.SameSite
}

在这种情况下,Path现在为空。稍后在创建实际的HTTP Cookie并发送它时。组合Cookie字符串的逻辑是

// Save 将单个会话添加到响应中。
func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
	session *Session) error {
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
		s.Codecs...)
	if err != nil {
		return err
	}
	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
	return nil
}
func newCookieFromOptions(name, value string, options *Options) *http.Cookie {
	return &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     options.Path,
		Domain:   options.Domain,
		MaxAge:   options.MaxAge,
		Secure:   options.Secure,
		HttpOnly: options.HttpOnly,
		SameSite: options.SameSite,
	}

}
func (c *Cookie) String() string {
   ...

   if len(c.Path) > 0 {
	b.WriteString("; Path=")
	b.WriteString(sanitizeCookiePath(c.Path))
   }

   ...
}

为了纠正这个问题,我需要在设置选项时设置其他字段。

session.Options(sessions.Options{
	MaxAge: 60 * 2, // 这将会话设置为在600秒(10分钟)后过期
	// 添加这些设置以确保正确的Cookie行为:
	Path:     "/",  // 使Cookie可用于所有路径
	HttpOnly: true, // 防止XSS
	Secure:   true, // 用于HTTPS
	SameSite: http.SameSiteStrictMode,
})

之后,重新启动并再次运行流程,它就可以工作了。Cookie看起来像这样

SetCookie: mysecurity=MTczMjcwNzA3MHxEWDhFUVRQUFBcV80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQW9BQ0hCcGEyVXdNREF5fMXu2tVBJMAYJbOxou9ZVNYMuTuU1H7XrV7KU2cQ3S6x; Path=/; Expires=Wed, 27 Nov 2024 11:41:10 GMT; Max-Age=600; HttpOnly; Secure; SameSite=Strict

Path和其他字段都存在。

虽然问题已解决。为什么原来的解决方案不起作用?或者至少我希望它能工作。这导致我对Gin中session模块的设计产生了一些想法。

  1. 如果未设置/为空,为什么某些字段没有默认值?当创建会话结构时,这些字段确实有默认值。
  2. 它是否应该提供WithOptions模式,以便在必要时设置单个字段,因为有时我可能只想更改MaxAge字段。

希望这对遇到相同问题的用户有所帮助。

SESSION  GO  GOLANG  COOKIE  NOT WORKING  GIN  MISSING 

       

  RELATED


  0 COMMENT


No comment for this article.



  RANDOM FUN

Quit Vim on Mac