From d4bb96fbcb29b1839b0c3a5347fb3f75a40dae5a Mon Sep 17 00:00:00 2001 From: Nick Zeng Date: Mon, 9 Mar 2026 14:15:11 +0800 Subject: [PATCH] update permission middleware --- .../app/Middleware/PermissionMiddleware.php | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 backend/app/Middleware/PermissionMiddleware.php diff --git a/backend/app/Middleware/PermissionMiddleware.php b/backend/app/Middleware/PermissionMiddleware.php new file mode 100644 index 0000000..06e6bc8 --- /dev/null +++ b/backend/app/Middleware/PermissionMiddleware.php @@ -0,0 +1,228 @@ +auth->guard('jwt')->user(); + if (!$user) { + return $handler->handle($request); + } + + // 获取用户 scope(含角色和 bitmap) + $user_scope = $this->scopeTableManager->getUserScope($user->id); + if (!$user_scope) { + return $this->forbiddenResponse('用户权限未配置'); + } + + $role = $user_scope['role']; + $method = $request->getMethod(); + $path = $request->getUri()->getPath(); + + // ===== Step 1: 路由访问检查 ===== + if ($role !== 'administrator') { + $access_result = $this->checkRouteAccess($user->role_id, $method, $path); + if ($access_result === false) { + return $this->forbiddenResponse('无权访问该接口'); + } + } + + // ===== Step 2: 数据范围检查 ===== + $scope_result = $this->applyDataScope($request, $role, $user_scope['scope'], $user); + if ($scope_result instanceof ResponseInterface) { + return $scope_result; + } + + return $handler->handle($scope_result); + } + + /** + * 路由访问检查 + * + * @param int $role_id + * @param string $method + * @param string $path + * @return bool + */ + protected function checkRouteAccess(int $role_id, string $method, string $path): bool + { + // 查找路由记录 + $route = Route::query()->where('method', $method)->where('path', $path)->first(); + if (!$route) { + // 未注册到 routes 表的路由默认放行 + return true; + } + + // 1. 先查 override(优先级最高) + $override = RoleRouteOverride::query() + ->where('role_id', $role_id) + ->where('route_id', $route->id) + ->first(); + if ($override) { + return $override->allowed; + } + + // 2. 查 route group 授权 + if (!$route->group_id) { + // 路由未分组且无 override → 拒绝 + return false; + } + + return Db::table('role_route_groups') + ->where('role_id', $role_id) + ->where('group_id', $route->group_id) + ->exists(); + } + + /** + * 数据范围检查与注入 + * + * @return ServerRequestInterface|ResponseInterface + */ + protected function applyDataScope( + ServerRequestInterface $request, + string $role, + string $bitmap, + mixed $user, + ): ServerRequestInterface|ResponseInterface { + $params = $request->getQueryParams(); + $company_id = isset($params['company_id']) ? (int) $params['company_id'] : null; + $platform_id = isset($params['platform_id']) ? (int) $params['platform_id'] : null; + $store_id = isset($params['store_id']) ? (int) $params['store_id'] : null; + + // --- administrator: 全部放行 --- + if ($role === 'administrator') { + $request = $request->withAttribute('scope_type', 'all'); + $request = $request->withAttribute('scope_ids', []); + return $request; + } + + // --- developer: 基于维护的 platform --- + if ($role === 'developer') { + return $this->applyDeveloperScope($request, $user, $bitmap, $company_id, $platform_id, $store_id); + } + + // --- accessor: 基于 bitmap store_ids --- + return $this->applyAccessorScope($request, $bitmap, $company_id, $platform_id, $store_id); + } + + /** + * developer 角色的数据范围处理 + * + * @return ServerRequestInterface|ResponseInterface + */ + protected function applyDeveloperScope( + ServerRequestInterface $request, + mixed $user, + string $bitmap, + ?int $company_id, + ?int $platform_id, + ?int $store_id, + ): ServerRequestInterface|ResponseInterface { + $platform_ids = Platform::query()->where('developer_id', $user->id)->pluck('id')->toArray(); + + // 有 scope 参数时校验 + if ($platform_id !== null && !in_array($platform_id, $platform_ids, true)) { + return $this->forbiddenResponse('平台不在开发者权限范围内'); + } + if ($store_id !== null && !$this->bitmapService->has($bitmap, $store_id)) { + return $this->forbiddenResponse('店铺不在开发者权限范围内'); + } + if ($company_id !== null) { + $has_store = Store::query() + ->where('company_id', $company_id) + ->whereIn('platform_id', $platform_ids) + ->exists(); + if (!$has_store) { + return $this->forbiddenResponse('公司不在开发者权限范围内'); + } + } + + // 注入 scope + if ($platform_id === null && $store_id === null && $company_id === null) { + $request = $request->withAttribute('scope_type', 'platform'); + $request = $request->withAttribute('scope_ids', $platform_ids); + } else { + $request = $request->withAttribute('scope_type', 'store'); + $request = $request->withAttribute('scope_ids', $this->bitmapService->decode($bitmap)); + } + + return $request; + } + + /** + * accessor 角色的数据范围处理 + * + * @return ServerRequestInterface|ResponseInterface + */ + protected function applyAccessorScope( + ServerRequestInterface $request, + string $bitmap, + ?int $company_id, + ?int $platform_id, + ?int $store_id, + ): ServerRequestInterface|ResponseInterface { + $allowed_store_ids = $this->bitmapService->decode($bitmap); + + // 有 scope 参数时校验 + if ($store_id !== null && !$this->bitmapService->has($bitmap, $store_id)) { + return $this->forbiddenResponse('店铺不在访问权限范围内'); + } + if ($company_id !== null) { + $company_stores = Store::query()->where('company_id', $company_id)->pluck('id')->toArray(); + if (empty(array_intersect($company_stores, $allowed_store_ids))) { + return $this->forbiddenResponse('公司不在访问权限范围内'); + } + } + if ($platform_id !== null) { + $platform_stores = Store::query()->where('platform_id', $platform_id)->pluck('id')->toArray(); + if (empty(array_intersect($platform_stores, $allowed_store_ids))) { + return $this->forbiddenResponse('平台不在访问权限范围内'); + } + } + + // 注入已验证的 store_ids + $request = $request->withAttribute('scope_type', 'store'); + $request = $request->withAttribute('scope_ids', $allowed_store_ids); + + return $request; + } + + /** + * 返回 403 响应 + */ + protected function forbiddenResponse(string $message): ResponseInterface + { + return $this->response->json([ + 'code' => 403, + 'message' => $message, + ])->withStatus(403); + } +}