如何在Laravel中基于Eloquent实现分角色用户授权
核心问题解答
该场景完全不需要强制使用Laravel的Gate授权机制。
Gate本质是面向全局场景的授权规则注册入口(比如判断用户能不能访问系统设置页这类不绑定具体模型的权限),本场景是围绕Member模型的省份维度做数据权限隔离,更适合用全局查询作用域+请求参数校验的轻量方案实现,代码复用性更高、漏写权限判断的风险更低。
完整实现流程
前置步骤:补全模型关联关系
先把各模型的关联逻辑补全,为后续权限判断提供基础:
User模型添加两个关联方法:role():定义belongsTo关联,对应roles表的外键role_idoperatorAccess():定义hasMany关联,对应OperatorAccess模型的外键user_id
- 在
User模型中封装公共权限判断方法,统一获取当前用户可操作的省份集合,可加缓存减少重复查询:public function getAuthorizedProvincesAttribute() { // admin角色无省份限制,返回特殊标记 if ($this->role->role === 'admin') { return collect(['*']); } // operator角色返回自身绑定的所有省份 return $this->operatorAccess->pluck('province'); }
第一步:查询层权限隔离(解决列表查看越权问题)
在Member模型中定义全局查询作用域,从数据库查询层面直接过滤当前用户无权访问的省份数据,避免每个控制器单独写where条件漏判:
// Member模型内定义作用域 public function scopeAccessibleByUser($query, $user) { // admin不过滤任何数据 if ($user->authorized_provinces->contains('*')) { return $query; } // operator仅匹配自身有权限的省份 return $query->whereIn('province', $user->authorized_provinces); }
所有查询成员数据的接口,直接链式调用该作用域即可:Member::accessibleByUser(auth()->user())->paginate()
第二步:写入操作权限校验(解决新增、更新、删除越权问题)
- 新增/更新操作:在对应的FormRequest校验类中,校验用户提交的
province参数是否在自身权限范围内:// StoreMemberRequest / UpdateMemberRequest 类内的校验逻辑 public function rules() { $user = auth()->user(); // admin无限制 if ($user->authorized_provinces->contains('*')) { return []; } return [ 'province' => [ 'required', function ($attribute, $value, $fail) use ($user) { if (!$user->authorized_provinces->contains($value)) { $fail('你没有权限操作该省份的成员数据'); } } ] ]; } - 删除操作:删除前先通过前面定义的作用域查询对应成员记录,如果查询不到则直接返回403,避免用户通过修改ID删除其他省份的成员:
// 控制器内删除逻辑示例 public function destroy(Member $member) { $user = auth()->user(); // 如果当前用户没有权限操作这条记录(对应省份不匹配) if (!Member::accessibleByUser($user)->where('id', $member->id)->exists()) { abort(403, '无权限操作该数据'); } $member->delete(); return back(); }
可选的优化方向
如果后续业务扩展出更多权限规则(比如新增角色、开放字段级编辑权限),可以把散写的省份判断逻辑收敛到MemberPolicy类中,配合Laravel控制器的authorize()方法统一调用,全程不需要定义全局的Gate规则,维护成本更低。
内容的提问来源于stack exchange,提问作者Ismynr
相关产品推荐
相关产品推荐

