package main import ( "net/http" "time" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" ) var ( mockUsers = map[string]string{ "zhangsan": "123456", "lisi": "654321", } // secretKey = "your-secret-key" hmacSecretKey = "5KiinhGcvoXFmgOttvgMWd9ugadE0KVP" blockSecretKey = "OLZmfTWHGyjCfJoH1Y8SjbSY" ) const sessionTTL = 60 * 5 func sessionOptions(maxAge int) sessions.Options { return sessions.Options{ Path: "/", Domain: "", // 设置为 "" 表示当前域名 MaxAge: maxAge, // 单位为秒。0 表示不写Max-Age(浏览器会话cookie); -1 表示删除 cookie Secure: false, // 生产走 HTTPS 时改 true HttpOnly: true, // true 表示禁止 JS 读取 cookie SameSite: http.SameSiteStrictMode, // 严格模式,仅在同源请求中发送 cookie } } // 代码来自文章 https://www.cnblogs.com/XY-Heruo/p/22864951 func main() { r := gin.Default() // Create cookie-based session store with a secret key // 传单个密钥时, 只做签名认证,不加密cookie内容. 认证密钥建议 32 或 64 字节 // 传一对密钥时,第一个用于认证(HMAC),第二个用于加密(AES-128, AES-192, AES-256)。加密密钥必须是16、24或32字节长度,对应 AES-128/192/256。 // 多对密钥用于密钥轮换,第一对用于当前加密和认证,后续的用于解密和认证旧的cookie。Example: cookie.NewStore([]byte("newAuth"), []byte("newEnc"), []byte("oldAuth"), []byte("oldEnc")) store := cookie.NewStore([]byte(hmacSecretKey), []byte(blockSecretKey)) store.Options(sessionOptions(sessionTTL)) // 全局设置 cookie 过期时间为 5 分钟 r.Use(sessions.Sessions("mysession", store)) r.POST("/login", login) auth := r.Group("/", authRequired()) auth.GET("/profile", profile) auth.POST("/logout", logout) r.Run(":8080") } func login(c *gin.Context) { session := sessions.Default(c) username := c.PostForm("username") password := c.PostForm("password") if username == "" || password == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "username or password is empty"}) return } if mockUsers[username] != password { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"}) return } session.Set("user", username) session.Set("expire_at", time.Now().Add(time.Second*sessionTTL).Unix()) // 设置过期时间 if err := session.Save(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "save session failed"}) return } c.JSON(http.StatusOK, gin.H{"message": "logged in"}) } func logout(c *gin.Context) { session := sessions.Default(c) session.Clear() session.Options(sessionOptions(-1)) // 让浏览器删除 cookie if err := session.Save(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "save session failed"}) return } c.JSON(http.StatusOK, gin.H{"message": "logged out"}) } func profile(c *gin.Context) { session := sessions.Default(c) user := session.Get("user").(string) c.JSON(http.StatusOK, gin.H{"user": user}) } func authRequired() gin.HandlerFunc { return func(c *gin.Context) { session := sessions.Default(c) if session.Get("user") == nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } if expireAt, ok := session.Get("expire_at").(int64); !ok || time.Now().Unix() > expireAt { session.Clear() session.Options(sessionOptions(-1)) session.Save() c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "expired session"}) return } c.Next() } }