多用户组角色合并异常:单用户多组权限未整合的技术修复请求
需求
实现合并用户所属全部用户组角色的功能,确保单个用户能获取所有组的权限。
现有PHP代码
GetRoles方法
public function GetRoles($id = 'new', $module = null) { $json = []; $groups = array_merge((array) ($this->auth['payload']['group'] ?? null), (array) ($this->auth['payload']['additionalGroup'] ?? null)); $access = GetAccess($module ?? $this->module, $groups); $json['insert'] = ($id === 'new' && in_array('insert', $access)) ? true : null; $json['view'] = ($id === 'view' && in_array('view', $access)) ? true : null; $json['update'] = ($id !== 'new' && in_array('update', $access)) ? true : null; $json['delete'] = ($id !== 'new' && in_array('delete', $access)) ? true : null; $json['upload'] = ($id !== 'new' && in_array('upload', $access)) ? true : null; $json['remove'] = ($id !== 'new' && in_array('remove', $access)) ? true : null; $json['inactive'] = ($id !== 'new' && in_array('inactive', $access)) ? true : null; $json['approve'] = ($id !== 'new' && in_array('approve', $access)) ? true : null; $json['cancel'] = ($id !== 'new' && in_array('cancel', $access)) ? true : null; $json['reopen'] = ($id !== 'new' && in_array('reopen', $access)) ? true : null; return $json; }
GetAccess函数
function GetAccess($module, $groups, $role = null) { global $config; $rules = []; $modules = ModuleMerge($config['modules']); if (isset($config['access'][$module])) { foreach ($config['access'][$module] ?? [] as $key => $value) { if (in_array($key, $groups)) { $rules = array_unique(array_merge($rules, $value)); } } } else if (in_array($module, ['main', 'login', 'login_google', 'register', 'reset', 'newpassword', 'validate', 'contact_us', 'disclaimer', 'privacy_policy', 'security_policy', 'terms_condition', 'help'])) { $rules = ['view', 'insert', 'update']; } else { $rules = $modules[$module]; } if ($role) { $rules = in_array($role, $rules); } return $rules; }
ModuleMerge函数
function ModuleMerge($array) { $result = []; for ($i = 0; $i < count($array); $i++) { $moduleKey = $array[$i]['module'] ?? null; if (!isset($result[$moduleKey])) { $result[$moduleKey] = []; } $result[$moduleKey] = $result[$moduleKey] ? array_unique(array_merge($result[$moduleKey], $array[$i]['roles'] ?? [])) : ($array[$i]['roles'] ?? []); if (is_array($array[$i]['children'] ?? null)) { $result = array_merge($result, ModuleMerge($array[$i]['children'])); } } return $result; }
场景与问题
场景
用户1所属主组为['Staff'],附加组为['Store Admin', 'Merchandise', 'Inventory SV'];各角色组权限分别为:
- Staff组:
['View main dashboard', 'edit profile', 'upload profile picture'] - Store Admin组:
['View store module page', 'add store item', 'update store item', 'delete store item', 'approve store item'] - Merchandise组:
['View merchandise stock', 'insert merchandise recipient']
问题
当前用户1仅能访问主组角色权限,附加组角色未被合并,实际结果为['View main dashboard', 'edit profile', 'upload profile picture'],期望结果为整合所有组的角色权限:['View main dashboard', 'edit profile', 'upload profile picture', 'View store module page', 'add store item', 'update store item', 'delete store item', 'approve store item', 'View merchandise stock', 'insert merchandise recipient']
问题排查与修复
核心问题分析
- 组合并逻辑潜在漏洞:原代码虽用
array_merge合并主组与附加组,但未做严格的空值校验,若additionalGroup为空或格式错误,会导致附加组未被纳入权限计算。 - 权限查询范围限制:原
GetAccess仅查询指定模块下的组权限,附加组的权限若属于其他模块,当前模块的查询无法覆盖这些内容。 - 组名匹配一致性问题:若
$config['access']中的组名与用户实际组名存在大小写、空格差异,会导致匹配失败,附加组权限无法被合并。
修复方案
1. 优化用户组合并逻辑
在GetRoles中增加空值判断与去重,确保所有有效组都被纳入计算:
public function GetRoles($id = 'new', $module = null) { $json = []; $groups = []; // 处理主组 $mainGroup = $this->auth['payload']['group'] ?? null; if (!empty($mainGroup)) { $groups = array_merge($groups, (array)$mainGroup); } // 处理附加组 $additionalGroups = $this->auth['payload']['additionalGroup'] ?? null; if (!empty($additionalGroups)) { $groups = array_merge($groups, (array)$additionalGroups); } // 去重避免重复组干扰 $groups = array_unique($groups); // 传入null获取所有模块的权限 $access = GetAccess($module ?? null, $groups); $json['insert'] = ($id === 'new' && in_array('insert', $access)) ? true : null; $json['view'] = ($id === 'view' && in_array('view', $access)) ? true : null; $json['update'] = ($id !== 'new' && in_array('update', $access)) ? true : null; $json['delete'] = ($id !== 'new' && in_array('delete', $access)) ? true : null; $json['upload'] = ($id !== 'new' && in_array('upload', $access)) ? true : null; $json['remove'] = ($id !== 'new' && in_array('remove', $access)) ? true : null; $json['inactive'] = ($id !== 'new' && in_array('inactive', $access)) ? true : null; $json['approve'] = ($id !== 'new' && in_array('approve', $access)) ? true : null; $json['cancel'] = ($id !== 'new' && in_array('cancel', $access)) ? true : null; $json['reopen'] = ($id !== 'new' && in_array('reopen', $access)) ? true : null; return $json; }
2. 修改GetAccess支持全模块权限查询
调整GetAccess函数,增加全模块权限收集逻辑,当$module为null时遍历所有模块的权限配置:
function GetAccess($module = null, $groups, $role = null) { global $config; $rules = []; $modules = ModuleMerge($config['modules']); if ($module === null) { // 遍历所有模块,收集用户所有组的权限 foreach ($config['access'] ?? [] as $mod => $groupPermissions) { foreach ($groupPermissions as $key => $value) { if (in_array($key, $groups)) { $rules = array_unique(array_merge($rules, $value)); } } } } else if (isset($config['access'][$module])) { // 查询指定模块下的用户组权限 foreach ($config['access'][$module] ?? [] as $key => $value) { if (in_array($key, $groups)) { $rules = array_unique(array_merge($rules, $value)); } } } else if (in_array($module, ['main', 'login', 'login_google', 'register', 'reset', 'newpassword', 'validate', 'contact_us', 'disclaimer', 'privacy_policy', 'security_policy', 'terms_condition', 'help'])) { $rules = ['view', 'insert', 'update']; } else { $rules = $modules[$module] ?? []; } if ($role) { $rules = in_array($role, $rules); } return $rules; }
3. 验证组名匹配一致性
检查$config['access']中的组名与用户实际组名是否完全一致(包括大小写、空格),例如确保'Store Admin'在配置中与用户组名完全相同,避免匹配失败。
修复效果验证
修复后,用户1的权限将合并所有所属组的权限,得到期望的结果:['View main dashboard', 'edit profile', 'upload profile picture', 'View store module page', 'add store item', 'update store item', 'delete store item', 'approve store item', 'View merchandise stock', 'insert merchandise recipient']
内容的提问来源于stack exchange,提问作者Developer MBAS

