Golang中如何设置会话Cookie的"domain"属性

Golang中如何设置会话Cookie的"domain"属性 你好,

我正在使用 Gin 框架配合 gin-contrib/sessions 进行会话管理。使用这个会话管理包时,会话 Cookie 会在响应中返回,但由于 gin-contrib/sessions 中存在一些错误,我无法在 Cookie 中设置“domain”属性。是否有其他可以用于会话管理的包,并且允许在会话 Cookie 中设置“domain”?

谢谢。

5 回复

谢谢Rolan,这个方法有效。不过,必须在会话选项中显式设置Path和MaxAge。

// 代码示例

更多关于Golang中如何设置会话Cookie的"domain"属性的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


通过 Options() 设置域名似乎工作正常:

	store := cookie.NewStore([]byte("secret"))
	store.Options(sessions.Options{Domain: "yourdomain.com"})

修改自 GitHub - gin-contrib/sessions: Gin middleware for session management 的示例:

https://www.screencast.com/t/imca3bisCAWQ

将 Cookie 的域名设置为 “bar.com” 可以使该 Cookie 在其子域名(如 “foo.bar.com” 和 “baz.bar.com”)上被访问。将其设置为子域名,例如 “foo.bar.com”,则 Cookie 仅在该子域名上可访问。

感谢Rolan的回复。

我采用了相同的方法,但没有使用cookies包,而是使用Redis来存储会话。在我这边,使用Redis时无法正常工作,HTTP响应中的Set-Cookie包含:Set-Cookie: mysession=; Domain=dev.com,即mysession为空。如果我使用cookie存储,则一切正常。

请查看以下代码,它在你那边能正常工作吗?

package main

import (
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/redis"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
    store.Options(sessions.Options{Domain: "dev.com"})

    r.Use(sessions.Sessions("mysession", store))

    r.GET("/incr", func(c *gin.Context) {
        session := sessions.Default(c)
        var count int
        v := session.Get("count")
        if v == nil {
            count = 0
        } else {
            count = v.(int)
            count++
        }
        session.Set("count", count)
        session.Save()
        c.JSON(200, gin.H{"count": count})
    })
    r.Run(":8000")
}

由于我们传递了一个新的 sessions.Options{} 结构体,并且 MaxAge 没有设置明确的值,它使用了零值,从而覆盖了创建存储时设置的默认值,这可能导致 MaxAge 被设置为零,并使您的会话自动过期:

}

// NewRediStoreWithPool 使用传入的 *redis.Pool 实例化一个 RediStore。
func NewRediStoreWithPool(pool *redis.Pool, keyPairs ...[]byte) (*RediStore, error) {
	rs := &RediStore{
		// http://godoc.org/github.com/gomodule/redigo/redis#Pool
		Pool:   pool,
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
		Options: &sessions.Options{
			Path:   "/",
			MaxAge: sessionExpire,
		},
		DefaultMaxAge: 60 * 20, // 20 分钟似乎是一个合理的默认值
		maxLength:     4096,
		keyPrefix:     "session_",
		serializer:    GobSerializer{},
	}
	_, err := rs.ping()
	return rs, err
}

尝试为会话过期指定一个 MaxAge 以及域名。该库使用的 MaxAge 默认值设置为 30 天,因此您可以使用该值。

	store.Options(sessions.Options{Domain: "dev.com", MaxAge: 86400 * 30})

我还没有深入研究您是否也需要将 Path 设置为 "/",但我认为如果 Path 为空,它会使用 "/"

在Golang中,你可以通过直接操作标准库的http.Cookie来设置会话Cookie的domain属性。虽然gin-contrib/sessions可能存在一些限制,但你可以通过自定义中间件或使用其他会话管理库来解决这个问题。

以下是一个使用标准库设置会话Cookie domain属性的示例:

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

func main() {
    r := gin.Default()
    
    r.GET("/set-cookie", func(c *gin.Context) {
        // 创建Cookie
        cookie := &http.Cookie{
            Name:     "session_id",
            Value:    "example_session_value",
            Domain:   ".example.com", // 设置domain属性
            Path:     "/",
            Expires:  time.Now().Add(24 * time.Hour),
            HttpOnly: true,
            Secure:   true, // 仅在HTTPS下传输
            SameSite: http.SameSiteLaxMode,
        }
        
        // 设置Cookie到响应
        http.SetCookie(c.Writer, cookie)
        
        c.JSON(http.StatusOK, gin.H{
            "message": "Cookie已设置",
        })
    })
    
    r.GET("/get-cookie", func(c *gin.Context) {
        // 读取Cookie
        cookie, err := c.Request.Cookie("session_id")
        if err != nil {
            c.JSON(http.StatusOK, gin.H{
                "message": "未找到Cookie",
            })
            return
        }
        
        c.JSON(http.StatusOK, gin.H{
            "cookie_value": cookie.Value,
            "cookie_domain": cookie.Domain,
        })
    })
    
    r.Run(":8080")
}

如果你需要更完整的会话管理功能,可以考虑使用gorilla/sessions库,它提供了更灵活的Cookie配置选项:

package main

import (
    "github.com/gorilla/sessions"
    "github.com/gin-gonic/gin"
    "net/http"
)

var store = sessions.NewCookieStore([]byte("your-secret-key"))

func main() {
    r := gin.Default()
    
    // 配置store的选项
    store.Options = &sessions.Options{
        Domain:   ".example.com", // 设置domain
        Path:     "/",
        MaxAge:   86400 * 7, // 7天
        HttpOnly: true,
        Secure:   true,
        SameSite: http.SameSiteLaxMode,
    }
    
    r.GET("/session", func(c *gin.Context) {
        // 获取会话
        session, err := store.Get(c.Request, "session-name")
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{
                "error": err.Error(),
            })
            return
        }
        
        // 设置会话值
        session.Values["authenticated"] = true
        session.Values["user_id"] = 123
        
        // 保存会话
        err = session.Save(c.Request, c.Writer)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{
                "error": err.Error(),
            })
            return
        }
        
        c.JSON(http.StatusOK, gin.H{
            "message": "会话已设置",
        })
    })
    
    r.Run(":8080")
}

对于gin-contrib/sessions,你也可以通过自定义存储实现来设置domain属性:

package main

import (
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    
    store := cookie.NewStore([]byte("secret"))
    
    // 自定义会话中间件
    r.Use(func(c *gin.Context) {
        session := sessions.Default(c)
        
        // 设置会话值
        session.Set("user", "john")
        
        // 获取底层session对象来设置Cookie选项
        if s, ok := session.(interface{ Options(sessions.Options) }); ok {
            s.Options(sessions.Options{
                Domain:   ".example.com", // 设置domain
                Path:     "/",
                MaxAge:   86400,
                HttpOnly: true,
                Secure:   true,
                SameSite: http.SameSiteLaxMode,
            })
        }
        
        session.Save()
        
        c.Next()
    })
    
    r.GET("/test", func(c *gin.Context) {
        session := sessions.Default(c)
        user := session.Get("user")
        
        c.JSON(http.StatusOK, gin.H{
            "user": user,
        })
    })
    
    r.Run(":8080")
}

这些方法都可以在会话Cookie中设置domain属性。gorilla/sessions库通常被认为是更稳定和功能更完整的解决方案,特别是在需要精细控制Cookie属性时。

回到顶部