Laravel Policy与Show方法权限控制问题:用户数据隔离验证
我用Laravel Policy做数据权限控制,所有表都有user_id字段,通过对比当前登录用户ID和数据的user_id实现验证。现在有Objekt和ObjektVerwaltung两张表,通过objekt_id外键关联,需要确保用户只能查看和自己有权限的Objekt关联的ObjektVerwaltung数据。
现有代码
ObjektVerwaltung控制器的show方法
public function show($objektvwId) { $objektId = ObjektVerwaltung::with('Objekt')->find($objektvwId); $this->authorize('view', $objektId); $objekte = ObjektVerwaltung::where('objekt_id',$objektvwId)->get(); return view('objekte.verwaltung', compact('objekte')); }
Policy代码
public function view(User $user, ObjektVerwaltung $objektVerwaltung) { return $objektVerwaltung->user_id === $user->id; }
模型代码
ObjektVerwaltung模型
class ObjektVerwaltung extends Model { use HasFactory; protected $table = 'objekte_verwaltungens'; protected $fillable = ['user_id','objekt_id','key', 'value']; public function Objekt() { return $this->belongsTo(Objekt::class); } }
Objekt模型
class Objekt extends Model { use HasFactory; protected $table = 'objekts'; protected $fillable = ['name','strasse', 'hausnummer', 'plz', 'ort', 'user_id']; public function Mieter() { return $this->hasMany(Mieter::class); } public function Company() { return $this->belongTo(Company::class); } public function Objektverwaltung() { return $this->hasMany(ObjektVerwaltung::class); } }
遇到的问题
我知道用find()可以验证单条数据,但现在需要按objekt_id过滤一组ObjektVerwaltung数据,没法直接用find()。尝试用where()查询后调用authorize(),一直触发Unauthorized,甚至无法进入Policy逻辑。比如这段代码:
$objekt= ObjektVerwaltung::where('objekt_id', $objektId)->get(); $this->authorize('view', $objekt);
请求解决这个权限验证的问题。
解决方案
1. 修正Policy逻辑,关联验证Objekt的user_id
当前Policy只验证ObjektVerwaltung自身的user_id,但需求是验证关联的Objekt是否属于当前用户,调整Policy:
public function view(User $user, ObjektVerwaltung $objektVerwaltung) { // 验证关联的Objekt归属当前用户 return $objektVerwaltung->Objekt->user_id === $user->id; }
注意:为避免N+1查询,控制器查询时要提前预加载关联的Objekt:
ObjektVerwaltung::with('Objekt')->find($objektvwId);
2. 批量数据的权限验证:用viewAny方法或直接过滤
如果要验证一组ObjektVerwaltung数据(比如按objekt_id查询的集合),有两种可行方式:
方式一:定义viewAny方法
在ObjektVerwaltungPolicy中添加批量验证方法:
public function viewAny(User $user, $objektId) { // 先验证当前用户拥有该Objekt的权限 $objekt = Objekt::findOrFail($objektId); return $objekt->user_id === $user->id; }
控制器调用时传入Objekt模型类和objekt_id:
public function show($objektId) { $this->authorize('viewAny', [ObjektVerwaltung::class, $objektId]); // 查询该Objekt下的所有Verwaltung数据 $objekte = ObjektVerwaltung::where('objekt_id', $objektId)->get(); return view('objekte.verwaltung', compact('objekte')); }
方式二:直接在查询时过滤用户权限
更简洁的方式是通过双重条件查询确保数据安全,无需额外修改Policy:
public function show($objektId) { // 先确认Objekt属于当前用户,不存在则抛出404 $objekt = Objekt::where('id', $objektId)->where('user_id', auth()->id())->firstOrFail(); // 通过关联关系查询该Objekt下的所有Verwaltung数据 $objekte = $objekt->Objektverwaltung()->get(); return view('objekte.verwaltung', compact('objekte')); }
3. 修正控制器变量命名错误
原控制器中$objektId = ObjektVerwaltung::with('Objekt')->find($objektvwId);变量名混淆,$objektId实际是ObjektVerwaltung实例,应改为:
public function show($objektvwId) { $objektVerwaltung = ObjektVerwaltung::with('Objekt')->findOrFail($objektvwId); $this->authorize('view', $objektVerwaltung); // 通过关联获取该Objekt下的所有Verwaltung数据 $objekte = $objektVerwaltung->Objekt->Objektverwaltung; return view('objekte.verwaltung', compact('objekte')); }
内容的提问来源于stack exchange,提问作者JrWebDev

