// Package middleware 提供 HTTP 中间件。 package middleware import ( "strings" "github.com/gin-gonic/gin" "portal-mini-go/internal/pkg/errors" "portal-mini-go/internal/pkg/session" "portal-mini-go/internal/repository/miniapp" ) const ( appIDCtxKey = "app_id" miniIDCtxKey = "mini_id" userIDCtxKey = "user_id" ) // headerAppID 是小程序在请求头中携带的微信 AppID 字段名。 const HeaderAppID = "X-App-ID" // HeaderSessionToken 是小程序用户会话 token 的请求头字段名。 // 也兼容标准 Authorization: Bearer 形式。 const HeaderSessionToken = "X-Session-Token" // MiniAppAuth 校验请求头中的 app_id 是否对应一个已启用且未删除的被纳管小程序, // 通过则将 app_id 与小程序的数据库主键注入 context,供后续 handler/service 使用。 // 用户身份最终由 (mini_id, open_id) 唯一确定。 func MiniAppAuth(repo *miniapp.Repository) gin.HandlerFunc { return func(c *gin.Context) { appID := c.GetHeader(HeaderAppID) if appID == "" { c.Error(errors.ErrBadRequest) c.Abort() return } mp, err := repo.GetActiveByAppID(c.Request.Context(), appID) if err != nil { c.Error(errors.ErrNotFound) c.Abort() return } c.Set(appIDCtxKey, appID) c.Set(miniIDCtxKey, mp.ID) c.Next() } } // AppID 从 gin.Context 取出已校验的 appID。 func AppID(c *gin.Context) string { if v, ok := c.Get(appIDCtxKey); ok { if s, ok := v.(string); ok { return s } } return "" } // MiniID 从 gin.Context 取出已校验的小程序数据库主键。 func MiniID(c *gin.Context) uint64 { if v, ok := c.Get(miniIDCtxKey); ok { if id, ok := v.(uint64); ok { return id } } return 0 } // MiniUserAuth 校验小程序用户会话 token(由 Login 签发、存于 session.Store), // 通过后将当前用户主键注入 context。必须在 MiniAppAuth 之后使用, // 依赖其注入的 mini_id 确保会话归属到正确的小程序。 // token 来源:请求头 Authorization: Bearer 或 X-Session-Token。 func MiniUserAuth(store session.Store) gin.HandlerFunc { return func(c *gin.Context) { token := SessionToken(c) if token == "" { c.Error(errors.ErrUnauthorized) c.Abort() return } userID, err := store.Get(c.Request.Context(), session.MaSessionKey(token)) if err != nil { c.Error(errors.ErrUnauthorized) c.Abort() return } c.Set(userIDCtxKey, userID) c.Next() } } // UserID 从 gin.Context 取出当前已认证用户的主键。 func UserID(c *gin.Context) uint64 { if v, ok := c.Get(userIDCtxKey); ok { if id, ok := v.(uint64); ok { return id } } return 0 } // SessionToken 从请求头提取小程序用户会话 token。 func SessionToken(c *gin.Context) string { if h := c.GetHeader("Authorization"); strings.HasPrefix(h, "Bearer ") { return strings.TrimSpace(strings.TrimPrefix(h, "Bearer ")) } return c.GetHeader(HeaderSessionToken) }