中间件不根据角色保护路由

Middleware doesn't protect routes based on role

我几个小时前提出了一个类似的问题,但没有收到太多答案,所以这就是我问同样问题的原因,但我会尝试用不同的措辞来表达它,也许让它更清楚,我只得到该问题的一个答案,但没有用。如果你想看的话,我会把 link 加到我之前的问题中。

Can't protect Routes based on role Laravel 8

我遇到的问题是,我正在尝试使用中间件来允许访问某些路由,前提是它们具有相应的角色。

我的中间件代码是这样的(在我上一个问题的答案中建议的那个)。

         <?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()->role($role)->get()) {
                        // 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);
        }
    }

不确定它是否可以帮助我的用户 table 在我的数据库中它是这样的

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role` varchar(20) DEFAULT NULL,
  `name` varchar(100) DEFAULT NULL,
  `email` varchar(100) DEFAULT NULL,
  `password` varchar(250) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `remember_token` varchar(255) DEFAULT NULL,
  `idPersona` int(11) DEFAULT NULL,
  `estado` tinyint(1) NOT NULL DEFAULT 31,
  `prueba` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `users_FK` (`idPersona`),
  CONSTRAINT `users_FK` FOREIGN KEY (`idPersona`) REFERENCES `personas` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8mb4

我正在用这条路由测试我的中间件,在这种情况下,我试图只让 administrador 访问该路由,如果没有,则只允许 return。 (中间件角色就是上面代码中的那个)

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

问题是所有用户都可以访问它。我认为这是一个逻辑问题,因为如果我在 if false 之外显示带有 dd() 的 if returns,即使在 administrador 角色中也是如此。

将您的中间件方法更改为:

public function handle(Request $request, Closure $next, $role)
{
    // dd($request->user()->role, $role); 
    // uncomment this for testing purpose if it's not working, both should be equal to "administrator"

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

您之前拥有的 $request->user()->role($role)->get() 将 return 管理员集合。

这不是您要找的。您只想检查当前用户 $request->user() 是否是管理员。

由于用户有一个 role 列,只需检查您提供的 $role 是否相同就足够了。

在英语中,if 语句表示“如果没有用户或用户的列 role 的值不等于 $role,则重定向回来”。


如果您需要多个角色怎么办?

使用可变参数函数 (https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list)。

在函数声明中添加... (...$roles) :

public function handle(Request $request, Closure $next, ...$roles)
{
    //dd($roles); 
    //is now an array with all the roles you provided to the route.

    if (!$request->user() || !in_array($request->user()->role, $roles)) {
        // Redirect...
                       
        return back();
    }
                        
    return $next($request);           
}

使用可变参数函数可以将字符串参数转换为数组。

在您的路线中,您现在可以添加多个角色:

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

在中间件中,$roles将等于:

['administrador', 'client', 'somethingelse']