update permission middleware
This commit is contained in:
@@ -0,0 +1,228 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Middleware;
|
||||||
|
|
||||||
|
use App\Model\Platform;
|
||||||
|
use App\Model\RoleRouteOverride;
|
||||||
|
use App\Model\Route;
|
||||||
|
use App\Model\Store;
|
||||||
|
use App\Service\ScopeBitmapService;
|
||||||
|
use App\Service\ScopeTableManager;
|
||||||
|
use Hyperf\DbConnection\Db;
|
||||||
|
use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Qbhy\HyperfAuth\AuthManager;
|
||||||
|
|
||||||
|
class PermissionMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected readonly AuthManager $auth,
|
||||||
|
protected readonly HttpResponse $response,
|
||||||
|
protected readonly ScopeTableManager $scopeTableManager,
|
||||||
|
protected readonly ScopeBitmapService $bitmapService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
|
{
|
||||||
|
// 获取已认证用户(由 AuthMiddleware 预先认证)
|
||||||
|
$user = $this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user