feat(ECR-006): 落地运营后台 Phase A(admin API + admin-h5)
新增独立鉴权的 /api/v1/admin 与 Vue 控制台;会员授予与审计同事务,并补集成/单测。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
const AdminIDKey ctxKey = "admin_id"
|
||||
const AdminTokenHeader = "Authorization"
|
||||
|
||||
// AdminSessionResolver looks up a valid admin session by token.
|
||||
type AdminSessionResolver interface {
|
||||
ResolveAdminID(ctx context.Context, token string) (uuid.UUID, error)
|
||||
}
|
||||
|
||||
// AdminAuth requires Bearer token for /admin routes.
|
||||
func AdminAuth(resolver AdminSessionResolver) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := BearerToken(c.GetHeader(AdminTokenHeader))
|
||||
if token == "" {
|
||||
response.Fail(c, http.StatusUnauthorized, 40101, "admin auth required")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
adminID, err := resolver.ResolveAdminID(c.Request.Context(), token)
|
||||
if err != nil || adminID == uuid.Nil {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set(string(AdminIDKey), adminID.String())
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// AdminIDFromContext returns the authenticated admin id.
|
||||
func AdminIDFromContext(c *gin.Context) (uuid.UUID, bool) {
|
||||
v, ok := c.Get(string(AdminIDKey))
|
||||
if !ok {
|
||||
return uuid.Nil, false
|
||||
}
|
||||
id, err := uuid.Parse(v.(string))
|
||||
return id, err == nil
|
||||
}
|
||||
|
||||
// BearerToken extracts an opaque token from Authorization (Bearer / bearer).
|
||||
func BearerToken(h string) string {
|
||||
h = strings.TrimSpace(h)
|
||||
if h == "" {
|
||||
return ""
|
||||
}
|
||||
lower := strings.ToLower(h)
|
||||
if strings.HasPrefix(lower, "bearer ") {
|
||||
return strings.TrimSpace(h[len("bearer "):])
|
||||
}
|
||||
return h
|
||||
}
|
||||
Reference in New Issue
Block a user