无法基于角色 Laravel 8 保护路由

Posted

技术标签:

【中文标题】无法基于角色 Laravel 8 保护路由【英文标题】:Can't protect Routes based on role Laravel 8 【发布时间】:2021-11-12 11:34:05 【问题描述】:

我正在尝试使用中间件来保护基于角色的路由。

我正在测试这条路线,只允许角色administrador 能够进入它。

Route::get('/gestionarMedicos', [PersonaController::class,'mostrarMedicos'])->name('personaMostrarMedicos')->middleware('auth','firstLogin','role:administrador');

这是我的中间件的代码(在路由中称为路由)

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureUserHasRole

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next, $role)
    

        if (! $request->user()->hasRole($role)) 
            // Redirect...
            return back();
        
        return $next($request);


    

这是用户模型的代码

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;

class User extends Authenticatable

    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    
    protected $fillable = [
        'role',
        'name',
        'email',
        'password',
        'idPersona',
        'estado'
    ];
    
    public function Persona()
        return $this->belongsTo(Persona::class,'idPersona');
    
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'remember_token'
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function hasRole($role)
    
        return User::where('role', $role)->get();
    

某些逻辑没有正确完成,因为即使没有该角色的用户我也可以访问路由

中间件中的 dd() 用于测试,我得到了“管理员”。

我已经尝试了 Patrick Obafemi 的解决方案,但我仍然遇到同样的问题。

为了测试,我根据 Patrcik 的回答结果做了一个 dd 判断,结果是假的。

我不确定逻辑问题在哪里。

如果对我有帮助,我还将发布我的数据库模型的图片。

【问题讨论】:

dd($role);你来这里做什么? 'administrador' 忘记删除有问题的,我用它来测试。将添加问题 【参考方案1】:

问题在下面的链接中得到解答。

Middleware doesn't protect routes based on role

它还介绍了在需要为多个角色执行此操作的情况下如何保护路由。该条件是错误的,因为它提供了具有管理员角色的用户的集合。条件应该是这样的,只允许期望的角色访问路由

if (!$request->user() || $request->user()->role != $role) 
        // Redirect...
                       
        return back();
    

对于多个角色,您可以访问答案说明如何允许多个所需角色访问路由的链接。

也许帕特里克的回答是正确的,但这里也回答了问题。

【讨论】:

我是提出这个问题并在我提供的链接中再次提问的人,并且首先在这里得到了正确的回答。现在我将在这里尝试更详细一点,但链接中的答案提供了完美的答案。【参考方案2】:

我认为您的错误来自用户模型中的 hasRole。结果 user() 已经可以访问您的用户模型,因此您可以将其设置为本地范围。您现在在中间件中执行的操作将返回类似这样的内容。因此,使用 get 函数将返回用户集合,但要检查该用户是否存在,您应该使用 first() 或者更好的是仍然存在()。您也不能在单个模型实体上调用 where ,这就是 $request->user()->role() 试图做的事情

User::User::where('role', $role)->get();

试试这个

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureUserHasRole

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next, $role)
    
        $id = $request->user()->id;
        if (!User::where([['id', $id],['role',$role]])->exists()) 
            // Redirect...
            return back();
        
        return $next($request);


    

那么你的模型会是这样的

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;

class User extends Authenticatable

    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    
    protected $fillable = [
        'role',
        'name',
        'email',
        'password',
        'idPersona',
        'estado'
    ];
    
    public function Persona()
        return $this->belongsTo(Persona::class,'idPersona');
    
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'remember_token'
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function scopeRole($query, $role)
    
        return $query->where('role', $role);
    

【讨论】:

我尝试复制您的代码,但它不起作用 将尝试用所发生的情况更新问题。 @DiegoUtreras 你已经解决了吗?因为我明白为什么你的代码不起作用。我会更新我的答案 已经在这里解决了。 ***.com/questions/69231478/…【参考方案3】:

甚至你的类图也有问题。如果有角色,那么应该有继承。你已经在这里如何实现这一点了。简而言之,解决方案是制作门或策略或中间件。我有一个中间件示例,但仅用于验证用户是否为管理员。

class VerifyIsAdmin

public function handle($request, Closure $next)

    $user = $request->user();
    if ($user && $user->role === 'admin') 
        return $next($request);
    
    return abort(403);


这涉及 verifyIsAdmin 中间件。然后你去

APP/Http/Kernel

然后粘贴

    protected $routeMiddleware = [
    'admin' => VerifyIsAdmin::class,
];

并在命名空间中添加以下行

use App\Http\Middleware\VerifyIsAdmin;

并添加到路由组

Route::middleware(['admin'])->group(function () 

【讨论】:

以上是关于无法基于角色 Laravel 8 保护路由的主要内容,如果未能解决你的问题,请参考以下文章

路由资源更新和删除无法使用除存储 laravel 8 之外的多个角色

Laravel 路由问题:自动重定向到根文件夹

在 laravel 中使用中间件保护路由

Laravel 无法保护 API 路由

我无法从 laravel 中的输入值中获取请求值

将参数传递给路由保护