72 lines
2.4 KiB
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()
|
|
}
|
|
}
|