首页 > 解决方案 > 基于角色特定能力的 Laravel 角色和权限

问题描述

我有一个项目,我希望特定用户查看特定页面,该特定用户具有查看角色,例如我有用户 1 具有管理员角色并且管理员角色能够在我的设计中查看此页面我制作了 3 个模型用户、角色和能力

用户模型:

<?php
    
namespace App;
    
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
    
class User extends Authenticatable
{
    use Notifiable;
    
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password','district','area','committee','position',
    ];
    
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
    
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
    
    public function answer()
    {
        return $this->hasMany('App\Answer');
    }
    
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
    
    public function hasRole($role)
    {
        if ($this->roles()->where('name', $role)->first()) {
            return true;
        }
    
        return false;
    }
    
    public function assignRole($role)
    {
        $this->roles()->save($role);
    }
}

好榜样:

<?php
    
namespace App;
   
use Illuminate\Database\Eloquent\Model;
    
class Role extends Model
{
    protected $fillable = ['name'];
    
    public function abilities()
    {
        return $this->belongsToMany('App\Ability');
    }
    
    public function hasAbility($ability)
    {
        if ($this->abilities()->where('name', $ability)->first()) {
            return true;
        }
        
        return false;
    }
      
    public function assignAbility($ability)
    {
        $this->abilities()->save($ability);
    }
    
    public function users()
    {
        return $this->belongsToMany('App\User');
    }    
}

能力模型:

<?php
    
namespace App;
    
use Illuminate\Database\Eloquent\Model;
  
class Ability extends Model
{
    protected $fillable = ['name'];
    
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}

这是我的用户策略:

<?php
    
namespace App\Policies;
    
use App\User;
use App\Role;
use Illuminate\Auth\Access\HandlesAuthorization;
    
class UserPolicy
{
    use HandlesAuthorization;
    
    public function view (Role $role)
    {
        return $role->hasAbility('view');
    }
    
    public function manage (User $user)
    {
        return true;
    }
    
    public function edit (User $user)
    {
        return true;
    }
    
    public function update (User $user)
    {
        return true;
    }
    
    public function add (User $user)
    {
        return true;
    }
}

和政策的控制者

<?php
    
namespace App\Http\Controllers;
    
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
    
use App\User;
use App\Role;
    
class MemberController extends Controller
{
    public function index(Role $role)
    {
        $this->authorize('view', $role);
    
        return view ('members.create')->with('users', User::all());
    }
    
    public function manage(User $user)
    {
        $this->authorize('manage', $user);
        
        return view ('members.manage')->with('users', User::all());
    }
    
    public function edit(User $user)
    {
        $this->authorize('edit', $user);
    
        return view ('members.edit')->with('user', User::all())->with('roles', Role::all());
    }
    
    public function update(Request $request, User $user)
    {
        $this->authorize('update', $user);
    
        $user->roles()->sync($request->roles);
    
        return redirect('/members/edit');
    
    }
    
    public function store(User $user)
    {
        $this->authorize('add', $user);
        $this->validate(request(), [
            'name'      => ['required', 'string', 'max:255'],
            'district'  => ['required', 'string', 'max:255'],
            'area'      => ['required', 'string', 'max:255'],
            'committee' => ['required', 'string', 'max:255'],
            'position'  => ['required', 'string', 'max:255'],
            'email'     => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password'  => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    
        $data = request()->all();
    
        $member = new User();
        $member->name      = $data['name'];
        $member->district  = $data['district'];
        $member->area      = $data['area'];
        $member->committee = $data['committee'];
        $member->position  = $data['position'];
        $member->email     = $data['email'];
        $member->password  = Hash::make($data['password']);
        
        $member->save();
    
        return redirect('/members/create');
    }
}

index 函数应该是与function viewin相关的函数UserPolicy ,它can位于我的 Blade.php 文件中

@can('view', \App\Role::class)
    <li class="">
        <a class="" href="/members/create">
            <span><i class="fa fa-user-plus" aria-hidden="true"></i></span>
            <span>Add Member</span>
        </a>
    </li>
@endcan

在策略中,当我将其链接到登录用户的角色名称时,一切正常,但如果我想将其链接到角色的能​​力,它就不起作用,所以关于View FunctionUserPolicy 中应该如何使用的任何想法被执行?

标签: laravelpolicy

解决方案


传递给策略的第一个参数是经过身份验证的User,而不是它的Role。我不认为它有效。也许如果您使用EXISTS查询重新实现。

public function view (User $user)
{
    return $user->roles()->whereHas('abilities', function ($ability) {
        $ability->where('name', 'view');
    })
    ->exists();
}

->exists()将查询转换为EXISTS查询,如果查询找到任何内容而无需返回任何行,它将返回一个布尔值。

https://laravel.com/docs/7.x/queries#aggregates

您可以将该逻辑放入User方法中。

# User model
public function hasAbility($ability): bool
{
    return $this->roles()->whereHas('abilities', function ($ability) {
        $ability->where('name', 'view');
    })
    ->exists();
}
public function view (User $user)
{
    return $user->hasAbility('view');
}

推荐阅读