425 lines
14 KiB
PHP
425 lines
14 KiB
PHP
|
|
<?php
|
|||
|
|
namespace app\common\traits;
|
|||
|
|
|
|||
|
|
use Illuminate\Database\Eloquent\Builder;
|
|||
|
|
use support\Db;
|
|||
|
|
use support\exception\BusinessException;
|
|||
|
|
|
|||
|
|
class DataPermission
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
ALTER TABLE `user` ADD COLUMN `report_to` bigint(20) unsigned DEFAULT '0' COMMENT '上级用户ID';
|
|||
|
|
|
|||
|
|
ALTER TABLE `roles`
|
|||
|
|
ADD COLUMN `parent_id` bigint(20) unsigned DEFAULT '0' COMMENT '父角色ID,0表示顶级角色',
|
|||
|
|
ADD INDEX `idx_parent_id` (`parent_id`) USING BTREE;
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用数据权限过滤到查询
|
|||
|
|
*
|
|||
|
|
* @param Builder $query 查询构造器
|
|||
|
|
* @param string $type 权限类型:superadmin, personal, same_role, role_tree, relation
|
|||
|
|
* @param int $currentUserId 当前用户ID
|
|||
|
|
* @param int $currentRoleId 当前角色ID
|
|||
|
|
* @param string $userField 用户ID字段名
|
|||
|
|
* @param string $roleField 角色ID字段名
|
|||
|
|
*/
|
|||
|
|
public static function applyQueryPermission(
|
|||
|
|
Builder $query,
|
|||
|
|
string $type,
|
|||
|
|
int $currentUserId,
|
|||
|
|
int $currentRoleId,
|
|||
|
|
string $userField = 'user_id',
|
|||
|
|
string $roleField = 'role_id'
|
|||
|
|
): void {
|
|||
|
|
// 超管不需要过滤
|
|||
|
|
if (self::isSuperAdmin($currentUserId)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch ($type) {
|
|||
|
|
case 'personal':
|
|||
|
|
// 个人权限:只能访问自己的数据
|
|||
|
|
$query->where($userField, $currentUserId);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'same_role':
|
|||
|
|
// 同角色权限:访问同角色的数据
|
|||
|
|
$userIds = self::getSameRoleUserIds($currentRoleId);
|
|||
|
|
$query->whereIn($userField, $userIds);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'role_tree':
|
|||
|
|
// 角色树权限:访问自己角色及子角色的数据
|
|||
|
|
$userIds = self::getRoleTreeUserIds($currentRoleId);
|
|||
|
|
$query->whereIn($userField, $userIds);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'relation':
|
|||
|
|
// 从属关系权限:访问自己及下属的数据
|
|||
|
|
$userIds = self::getRelationUserIds($currentUserId);
|
|||
|
|
$query->whereIn($userField, $userIds);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
// 默认个人权限
|
|||
|
|
$query->where($userField, $currentUserId);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用数据权限到创建/更新操作
|
|||
|
|
*
|
|||
|
|
* @param mixed $target 创建时为数组,更新时为Model对象
|
|||
|
|
* @param string $type 权限类型
|
|||
|
|
* @param int $currentUserId 当前用户ID
|
|||
|
|
* @param int $currentRoleId 当前角色ID
|
|||
|
|
* @param array $newData 更新时的数据
|
|||
|
|
* @param string $userField 用户ID字段名
|
|||
|
|
* @param string $roleField 角色ID字段名
|
|||
|
|
*/
|
|||
|
|
public static function applyMutationPermission(
|
|||
|
|
&$target,
|
|||
|
|
string $type,
|
|||
|
|
int $currentUserId,
|
|||
|
|
int $currentRoleId,
|
|||
|
|
array $newData = [],
|
|||
|
|
string $userField = 'admin_id',
|
|||
|
|
string $roleField = 'role_id'
|
|||
|
|
): void {
|
|||
|
|
// 超管不需要检查
|
|||
|
|
if (self::isSuperAdmin($currentUserId)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch ($type) {
|
|||
|
|
case 'personal':
|
|||
|
|
self::handlePersonalPermission($target, $newData, $userField, $currentUserId);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'same_role':
|
|||
|
|
self::handleSameRolePermission($target, $newData, $userField, $roleField, $currentRoleId);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'role_tree':
|
|||
|
|
self::handleRoleTreePermission($target, $newData, $userField, $roleField, $currentRoleId);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'relation':
|
|||
|
|
self::handleRelationPermission($target, $newData, $userField, $currentUserId);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
throw new BusinessException("未定义的数据权限类型: $type", 403);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取同角色的用户ID列表
|
|||
|
|
*
|
|||
|
|
* @param int $roleId 角色ID
|
|||
|
|
* @return array 用户ID列表
|
|||
|
|
*/
|
|||
|
|
public static function getSameRoleUserIds(int $roleId): array
|
|||
|
|
{
|
|||
|
|
return Db::table('user_roles')
|
|||
|
|
->where('role_id', $roleId)
|
|||
|
|
->pluck('user_id')
|
|||
|
|
->toArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取角色树下的所有用户ID列表
|
|||
|
|
*
|
|||
|
|
* @param int $roleId 当前角色ID
|
|||
|
|
* @return array 用户ID列表
|
|||
|
|
*/
|
|||
|
|
public static function getRoleTreeUserIds(int $roleId): array
|
|||
|
|
{
|
|||
|
|
// 获取角色树中的所有角色ID
|
|||
|
|
$roleIds = self::getRoleTreeIds($roleId);
|
|||
|
|
|
|||
|
|
// 获取这些角色下的所有用户ID
|
|||
|
|
return Db::table('user_roles')
|
|||
|
|
->whereIn('role_id', $roleIds)
|
|||
|
|
->pluck('user_id')
|
|||
|
|
->toArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取从属关系下的用户ID列表(包括自己和下属)
|
|||
|
|
*
|
|||
|
|
* @param int $userId 当前用户ID
|
|||
|
|
* @return array 用户ID列表
|
|||
|
|
*/
|
|||
|
|
public static function getRelationUserIds(int $userId): array
|
|||
|
|
{
|
|||
|
|
// 获取所有下属用户ID(递归)
|
|||
|
|
$subordinateIds = self::getSubordinateIds($userId);
|
|||
|
|
|
|||
|
|
// 返回自己和所有下属的ID
|
|||
|
|
return array_merge([$userId], $subordinateIds);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取直接下属的用户ID列表
|
|||
|
|
*
|
|||
|
|
* @param int $userId 用户ID
|
|||
|
|
* @return array 直接下属用户ID列表
|
|||
|
|
*/
|
|||
|
|
public static function getDirectSubordinateIds(int $userId): array
|
|||
|
|
{
|
|||
|
|
return Db::table('user')
|
|||
|
|
->where('report_to', $userId)
|
|||
|
|
->pluck('id')
|
|||
|
|
->toArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 递归获取所有下属用户ID列表
|
|||
|
|
*
|
|||
|
|
* @param int $userId 用户ID
|
|||
|
|
* @return array 所有下属用户ID列表
|
|||
|
|
*/
|
|||
|
|
private static function getSubordinateIds(int $userId): array
|
|||
|
|
{
|
|||
|
|
$subordinates = self::getDirectSubordinateIds($userId);
|
|||
|
|
$allSubordinates = $subordinates;
|
|||
|
|
|
|||
|
|
foreach ($subordinates as $subordinateId) {
|
|||
|
|
$allSubordinates = array_merge($allSubordinates, self::getSubordinateIds($subordinateId));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $allSubordinates;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理个人权限
|
|||
|
|
*/
|
|||
|
|
private static function handlePersonalPermission(
|
|||
|
|
&$target,
|
|||
|
|
array $newData,
|
|||
|
|
string $userField,
|
|||
|
|
int $currentUserId
|
|||
|
|
): void {
|
|||
|
|
if (is_array($target)) {
|
|||
|
|
// 创建时强制设置用户ID
|
|||
|
|
$target[$userField] = $currentUserId;
|
|||
|
|
} else {
|
|||
|
|
// 更新时校验原始记录
|
|||
|
|
if ($target->{$userField} !== $currentUserId) {
|
|||
|
|
throw new BusinessException("无权操作该记录", 403);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 禁止变更归属字段
|
|||
|
|
if (isset($newData[$userField]) && $newData[$userField] !== $currentUserId) {
|
|||
|
|
throw new BusinessException("无权变更归属字段", 403);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理同角色权限
|
|||
|
|
*/
|
|||
|
|
private static function handleSameRolePermission(
|
|||
|
|
&$target,
|
|||
|
|
array $newData,
|
|||
|
|
string $userField,
|
|||
|
|
string $roleField,
|
|||
|
|
int $currentRoleId
|
|||
|
|
): void {
|
|||
|
|
// 获取同角色用户ID列表
|
|||
|
|
$sameRoleUserIds = self::getSameRoleUserIds($currentRoleId);
|
|||
|
|
|
|||
|
|
if (is_array($target)) {
|
|||
|
|
// 创建时设置归属用户并校验
|
|||
|
|
if (isset($target[$userField])) {
|
|||
|
|
if (!in_array($target[$userField], $sameRoleUserIds)) {
|
|||
|
|
throw new BusinessException("无权为指定用户创建数据", 403);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$target[$userField] = $currentRoleId;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 更新时校验原始记录
|
|||
|
|
if (!in_array($target->{$userField}, $sameRoleUserIds)) {
|
|||
|
|
throw new BusinessException("无权操作该记录", 403);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 禁止变更归属字段到无权限的用户
|
|||
|
|
if (isset($newData[$userField]) && !in_array($newData[$userField], $sameRoleUserIds)) {
|
|||
|
|
throw new BusinessException("无权变更归属字段", 403);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理角色树权限
|
|||
|
|
*/
|
|||
|
|
private static function handleRoleTreePermission(
|
|||
|
|
&$target,
|
|||
|
|
array $newData,
|
|||
|
|
string $userField,
|
|||
|
|
string $roleField,
|
|||
|
|
int $currentRoleId
|
|||
|
|
): void {
|
|||
|
|
// 获取角色树用户ID列表
|
|||
|
|
$roleTreeUserIds = self::getRoleTreeUserIds($currentRoleId);
|
|||
|
|
|
|||
|
|
if (is_array($target)) {
|
|||
|
|
// 创建时校验用户是否在允许范围内
|
|||
|
|
if (isset($target[$userField])) {
|
|||
|
|
if (!in_array($target[$userField], $roleTreeUserIds)) {
|
|||
|
|
throw new BusinessException("无权为指定用户创建数据", 403);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$target[$userField] = $currentRoleId;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 更新时校验原始记录用户
|
|||
|
|
if (!in_array($target->{$userField}, $roleTreeUserIds)) {
|
|||
|
|
throw new BusinessException("无权操作该记录", 403);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 校验新用户是否在允许范围内
|
|||
|
|
if (isset($newData[$userField])) {
|
|||
|
|
if (!in_array($newData[$userField], $roleTreeUserIds)) {
|
|||
|
|
throw new BusinessException("无权变更用户字段", 403);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理从属关系权限
|
|||
|
|
*/
|
|||
|
|
private static function handleRelationPermission(
|
|||
|
|
&$target,
|
|||
|
|
array $newData,
|
|||
|
|
string $userField,
|
|||
|
|
int $currentUserId
|
|||
|
|
): void {
|
|||
|
|
// 获取从属关系用户ID列表
|
|||
|
|
$relationUserIds = self::getRelationUserIds($currentUserId);
|
|||
|
|
|
|||
|
|
if (is_array($target)) {
|
|||
|
|
// 创建时校验用户是否在允许范围内
|
|||
|
|
if (isset($target[$userField])) {
|
|||
|
|
if (!in_array($target[$userField], $relationUserIds)) {
|
|||
|
|
throw new BusinessException("无权为指定用户创建数据", 403);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$target[$userField] = $currentUserId;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 更新时校验原始记录用户
|
|||
|
|
if (!in_array($target->{$userField}, $relationUserIds)) {
|
|||
|
|
throw new BusinessException("无权操作该记录", 403);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 校验新用户是否在允许范围内
|
|||
|
|
if (isset($newData[$userField])) {
|
|||
|
|
if (!in_array($newData[$userField], $relationUserIds)) {
|
|||
|
|
throw new BusinessException("无权变更用户字段", 403);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取角色树ID(当前角色及其所有子角色)
|
|||
|
|
* 从数据库中动态获取角色树关系
|
|||
|
|
*/
|
|||
|
|
private static function getRoleTreeIds(int $roleId): array
|
|||
|
|
{
|
|||
|
|
static $roleTreeCache = [];
|
|||
|
|
|
|||
|
|
if (isset($roleTreeCache[$roleId])) {
|
|||
|
|
return $roleTreeCache[$roleId];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从数据库中递归获取角色树
|
|||
|
|
$ids = [$roleId];
|
|||
|
|
$queue = [$roleId];
|
|||
|
|
|
|||
|
|
while (!empty($queue)) {
|
|||
|
|
$current = array_shift($queue);
|
|||
|
|
// 查找当前角色的所有子角色
|
|||
|
|
$childRoles = Db::table('roles')
|
|||
|
|
->where('parent_id', $current)
|
|||
|
|
->where('status', 1) // 只获取启用的角色
|
|||
|
|
->pluck('id')
|
|||
|
|
->toArray();
|
|||
|
|
|
|||
|
|
foreach ($childRoles as $childId) {
|
|||
|
|
if (!in_array($childId, $ids)) {
|
|||
|
|
$ids[] = $childId;
|
|||
|
|
$queue[] = $childId;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$roleTreeCache[$roleId] = $ids;
|
|||
|
|
return $ids;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查是否为超管
|
|||
|
|
*
|
|||
|
|
* @param int $userId 用户ID
|
|||
|
|
* @return bool 是否为超管
|
|||
|
|
*/
|
|||
|
|
public static function isSuperAdmin(int $userId): bool
|
|||
|
|
{
|
|||
|
|
// 检查用户是否关联了超级管理员角色
|
|||
|
|
// 假设角色ID为1的是超级管理员角色
|
|||
|
|
$superAdminRoleId = 1;
|
|||
|
|
|
|||
|
|
return Db::table('user_roles')
|
|||
|
|
->where('user_id', $userId)
|
|||
|
|
->where('role_id', $superAdminRoleId)
|
|||
|
|
->exists();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取用户的角色ID
|
|||
|
|
*
|
|||
|
|
* @param int $userId 用户ID
|
|||
|
|
* @return int 角色ID
|
|||
|
|
*/
|
|||
|
|
public static function getUserIdRoleId(int $userId): int
|
|||
|
|
{
|
|||
|
|
// 从 user_roles 表中获取当前用户的角色ID
|
|||
|
|
$roleId = Db::table('user_roles')
|
|||
|
|
->where('user_id', $userId)
|
|||
|
|
->value('role_id');
|
|||
|
|
|
|||
|
|
return $roleId ?: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取用户的所有角色ID(包括继承的角色)
|
|||
|
|
*
|
|||
|
|
* @param int $userId 用户ID
|
|||
|
|
* @return array 角色ID列表
|
|||
|
|
*/
|
|||
|
|
public static function getUserAllRoleIds(int $userId): array
|
|||
|
|
{
|
|||
|
|
// 获取用户直接关联的角色
|
|||
|
|
$directRoleIds = Db::table('user_roles')
|
|||
|
|
->where('user_id', $userId)
|
|||
|
|
->pluck('role_id')
|
|||
|
|
->toArray();
|
|||
|
|
|
|||
|
|
// 获取所有继承的角色
|
|||
|
|
$allRoleIds = [];
|
|||
|
|
foreach ($directRoleIds as $roleId) {
|
|||
|
|
$allRoleIds = array_merge($allRoleIds, self::getRoleTreeIds($roleId));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 去重并返回
|
|||
|
|
return array_unique($allRoleIds);
|
|||
|
|
}
|
|||
|
|
}
|