新增独立鉴权的 /api/v1/admin 与 Vue 控制台;会员授予与审计同事务,并补集成/单测。 Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
1.6 KiB
Go
64 lines
1.6 KiB
Go
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
|
|
}
|