package middleware import ( "context" "net/http" "server/internal/db" "server/internal/db/sqlc" "server/internal/pkg/cache" "server/internal/pkg/cache/cachekey" "server/internal/pkg/errs" "server/internal/pkg/httputil" "strings" "github.com/go-chi/chi/v5" ) const ( IsAdminKey contextKey = "is_admin" ) type AuthMiddleware struct { store *db.Store cache *cache.Caches } func NewAuthMiddleware(store *db.Store, cache *cache.Caches) *AuthMiddleware { return &AuthMiddleware{ cache: cache, store: store, } } func UserIsAdmin(uid int32) bool { if uid == 1 { return true } return false } func IsAdmin(ctx context.Context) bool { isAdmin, ok := ctx.Value(IsAdminKey).(bool) return ok && isAdmin } func (m *AuthMiddleware) hasApiPermission(ctx context.Context, uid int32, requestMethod string, requestPath string) (bool, error) { var ( apis []sqlc.ListUserApisRow err error ) // 先从缓存中获取api数据 k := cachekey.UserApiPermissions(uid) apis, err = cache.GetOrSetJSON[[]sqlc.ListUserApisRow](ctx, m.cache, k, 0, func() ([]sqlc.ListUserApisRow, error) { return m.store.ListUserApis(ctx, uid) }) if err != nil { return false, err } for _, api := range apis { if api.Method != requestMethod { continue } if api.Path == requestPath { return true, nil } } return false, nil } func (m *AuthMiddleware) Middleware(router chi.Router) func(handler http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() claims, ok := GetClaims(ctx) if !ok || claims.UserID == 0 { httputil.Fail(w, errs.ErrUnauthorized) return } // 判断是否有管理员权限 isAdmin := UserIsAdmin(claims.UserID) if isAdmin { ctx = context.WithValue(ctx, IsAdminKey, isAdmin) next.ServeHTTP(w, r.WithContext(ctx)) return } // 如果不是管理员 需要判断api权限 rctx := chi.NewRouteContext() requestMethod := r.Method // https://github.com/go-chi/chi/pull/872 api := router.Find(rctx, requestMethod, r.URL.Path) requestPath := strings.TrimPrefix(api, "/api") hasPermission, err := m.hasApiPermission(ctx, claims.UserID, requestMethod, requestPath) if err != nil { httputil.Fail(w, err) return } if !hasPermission { httputil.Fail(w, errs.ErrPermissionDenied) return } next.ServeHTTP(w, r) }) } }