service.xpcool.com/internal/middleware/auth.go

72 lines
2.4 KiB
Go

package middleware
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
"service.xpcool.com/internal/consts"
"service.xpcool.com/internal/library/jwt"
"service.xpcool.com/internal/library/response"
"strings"
"time"
)
type contextKey string
const (
UserIDKey contextKey = "userId"
AdminIDKey contextKey = "adminId"
PermissionKey contextKey = "permission"
)
func bearer(r *ghttp.Request) string {
// 统一从 Authorization: Bearer <token> 提取令牌。
return strings.TrimSpace(strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer"))
}
func auditParam(raw string) string {
// 操作日志需要参数用于审计,但绝不记录密码、令牌和验证码等敏感字段。
lower := strings.ToLower(raw)
if strings.Contains(lower, "password") || strings.Contains(lower, "token") || strings.Contains(lower, "code") {
return "[redacted]"
}
return raw
}
func UserAuth(s *jwt.Service) ghttp.HandlerFunc {
// 用户端仅接受 scope=user 的 access token。
return func(r *ghttp.Request) {
c, err := s.Parse(bearer(r), "access", "user")
if err != nil {
response.JSON(r, consts.CodeUnauthorized, "login required", nil)
return
}
r.SetCtxVar(UserIDKey, c.Subject)
r.Middleware.Next()
}
}
func AdminAuth(s *jwt.Service, permissionCheck func(context.Context, uint64, string) (bool, error), audit func(context.Context, uint64, string, string, string, string, string, int, int)) ghttp.HandlerFunc {
// 管理端在令牌通过后继续校验 X-Permission 对应的 RBAC 权限,并在请求结束后记审计日志。
return func(r *ghttp.Request) {
start := time.Now()
c, err := s.Parse(bearer(r), "access", "admin")
if err != nil {
response.JSON(r, consts.CodeUnauthorized, "admin login required", nil)
return
}
permission := r.Header.Get("X-Permission")
if permission == "" {
response.JSON(r, consts.CodeForbidden, "permission identifier required", nil)
return
}
ok, err := permissionCheck(r.Context(), c.Subject, permission)
if err != nil || !ok {
response.JSON(r, consts.CodeAdminPermissionDenied, "permission denied", nil)
return
}
defer func() {
audit(r.Context(), c.Subject, permission, r.Method, r.URL.Path, r.GetClientIp(), auditParam(r.GetBodyString()), int(time.Since(start).Milliseconds()), r.Response.Status)
}()
r.SetCtxVar(AdminIDKey, c.Subject)
r.SetCtxVar(PermissionKey, permission)
r.Middleware.Next()
}
}