تعریف سطح دسترسی در لاراول

تعریف سطح دسترسی در لاراول

user Roles & Permissions

خب از اونجایی که هر وبسایتی پنل مدیریتی داره، و چندین مدیر باید اونو مدیریت کنن، خب لازم میشه که سطح دسترسی هایی تعریف بشه

بخاطر همین در لاراول از تکنیک های جالبی استفاده میشه

این راه حل رو از اینترنت پیدا کردم و منبع اصلیش رو هم قرار میدم.

1.ایجاد پروژه

برای این کار از دستور زیر استفاده میکنیم :

composer create-project --prefer-dist laravel/laravel blog

اسم پروژه blog در نظر میگیرم

2.ایجاد اهراز هویت کاربر

برای این کار از کد پیشفرض لاراول نسخه 6 به بالا استفاده میکنیم :

composer require laravel/ui --dev
php artisan ui vue --auth
npm install
npm run watch

برای نسخه 6 به پایین :

php artisan make:auth

3.ایجاد مدل ها

مدل های مورد نیاز رو ایجاد میکنیم:

php artisan make:model Permission -m
php artisan make:model Role -m

4.ویرایش Migrate ها

public function up()
    {
       Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email',191)->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
    });
}

 

 

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePermissionsTable extends Migration
{
    
    public function up()
    {
        Schema::create('permissions', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name'); // edit posts
            $table->string('slug'); //edit-posts
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('permissions');
    }
}
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateRolesTable extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name'); // edit posts
            $table->string('slug'); //edit-posts
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles');
    }
}

 

5.افزودن جدول میانی یا Pivot

این جدول در واقع ارتباط بین داده ها از جداول مختلف رو شامل میشه و برای رابطه های ManyToMany لازمه

فعلا جدول رابط دسترسی ها و کاربران رو ایجاد میکنیم:

php artisan make:migration create_users_permissions_table --create=users_permissions

فایل migration باید به این شکل باشه : 

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersPermissionsTable extends Migration
{
    public function up()
    {
        Schema::create('users_permissions', function (Blueprint $table) {
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('permission_id');

            //FOREIGN KEY CONSTRAINTS
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
 
            //SETTING THE PRIMARY KEYS
            $table->primary(['user_id','permission_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('users_permissions');
    }
}

الان نوبت ایجاد جدول افراد و رول ها هست :

php artisan make:migration create_users_roles_table --create=users_roles

فایل migration باید این شکلی باشه : 

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersRolesTable extends Migration
{
    public function up()
    {
        Schema::create('users_roles', function (Blueprint $table) {
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('role_id');

         //FOREIGN KEY CONSTRAINTS
           $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
           $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');

         //SETTING THE PRIMARY KEYS
           $table->primary(['user_id','role_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('users_roles');
    }
}

 

و در نهایت جدول میانی دسترسی ها و رول ها :

php artisan make:migration create_roles_permissions_table --create=roles_permissions

که دوباره migration رو تنظیم میکنیم :

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateRolesPermissionsTable extends Migration
{
    public function up()
    {
        Schema::create('roles_permissions', function (Blueprint $table) {
             $table->unsignedInteger('role_id');
             $table->unsignedInteger('permission_id');

             //FOREIGN KEY CONSTRAINTS
             $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
             $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');

             //SETTING THE PRIMARY KEYS
             $table->primary(['role_id','permission_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles_permissions');
    }
}

برای ایجاد دیتابیس از دستور زیر استفاده میکنیم :

php artisan migrate

6.تنظیم رابطه ها (Relations)

فایل های زیر رو مطابق نمونه ها تنظیم کنید :

App/Role.php

public function permissions() {

   return $this->belongsToMany(Permission::class,'roles_permissions');
       
}

public function users() {

   return $this->belongsToMany(User::class,'users_roles');
       
}

App/Permission.php

public function roles() {

   return $this->belongsToMany(Role::class,'roles_permissions');
       
}

public function users() {

   return $this->belongsToMany(User::class,'users_permissions');
       
}

7. ایجاد Trait

با استفاده از Trait می تونیم به مدل هامون توابعی جدید اضافی کنیم.

برای اینکار مراحل زیر رو طی میکنیم:

app/User.php

namespace App;

use App\Permissions\HasPermissionsTrait;

class User extends Authenticatable
{
    use HasPermissionsTrait; //Import The Trait
}

App/Permissions/HasPermissionsTrait.php

namespace App\Permissions;

use App\Permission;
use App\Role;

trait HasPermissionsTrait {

   public function givePermissionsTo(... $permissions) {

    $permissions = $this->getAllPermissions($permissions);
    dd($permissions);
    if($permissions === null) {
      return $this;
    }
    $this->permissions()->saveMany($permissions);
    return $this;
  }

  public function withdrawPermissionsTo( ... $permissions ) {

    $permissions = $this->getAllPermissions($permissions);
    $this->permissions()->detach($permissions);
    return $this;

  }

  public function refreshPermissions( ... $permissions ) {

    $this->permissions()->detach();
    return $this->givePermissionsTo($permissions);
  }

  public function hasPermissionTo($permission) {

    return $this->hasPermissionThroughRole($permission) || $this->hasPermission($permission);
  }

  public function hasPermissionThroughRole($permission) {

    foreach ($permission->roles as $role){
      if($this->roles->contains($role)) {
        return true;
      }
    }
    return false;
  }

  public function hasRole( ... $roles ) {

    foreach ($roles as $role) {
      if ($this->roles->contains('slug', $role)) {
        return true;
      }
    }
    return false;
  }

  public function roles() {

    return $this->belongsToMany(Role::class,'users_roles');

  }
  public function permissions() {

    return $this->belongsToMany(Permission::class,'users_permissions');

  }
  protected function hasPermission($permission) {

    return (bool) $this->permissions->where('slug', $permission->slug)->count();
  }

  protected function getAllPermissions(array $permissions) {

    return Permission::whereIn('slug',$permissions)->get();
    
  }

}

با این کار ما الان می تونیم کارهای زیر رو انجام بدیم :

$user = $request->user(); //getting the current logged in user
dd($user->hasRole('admin','editor')); // and so on

8.ایجاد CustomProvider

الان می خواهیم قابلیت بررسی دسترسی کاربر رو ایجاد کنیم.

برای این کار دستور زیر رو اجرا میکنیم : 

php artisan make:provider PermissionsServiceProvider

و برای اضافه کردن تابع can به کاربر دستورات زیر رو به Provider ای که ایجاد کردیم، اضافه میکنیم:

namespace App\Providers;

use App\Permission;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class PermissionsServiceProvider extends ServiceProvider
{
   
    public function register()
    {
        //
    }

    public function boot()
    {
        try {
            Permission::get()->map(function ($permission) {
                Gate::define($permission->slug, function ($user) use ($permission) {
                    return $user->hasPermissionTo($permission);
                });
            });
        } catch (\Exception $e) {
            report($e);
            return false;
        }

        //Blade directives
        Blade::directive('role', function ($role) {
             return "if(auth()->check() && auth()->user()->hasRole({$role})) :"; //return this if statement inside php tag
        });

        Blade::directive('endrole', function ($role) {
             return "endif;"; //return this endif statement inside php tag
        });

    }
}

و در نهایت provider رو ثبت کنیم :

config\app.php

'providers' => [

        App\Providers\PermissionsServiceProvider::class,
    
 ],

حالا می تونیم از can مثل مثال زیر استفاده کنیم :

dd($user->can('permission-slug'));

9.اضافه کردن اطلاعات تستی

یه کنترلری ایجاد میکنیم و کد های زیر رو داخلش مینویسیم :

Route::get('/roles', 'PermissionController@Permission');

App\Http\Controllers\PermissionController.php

namespace App\Http\Controllers;

use App\Permission;
use App\Role;
use App\User;
use Illuminate\Http\Request;

class PermissionController extends Controller
{   

    public function Permission()
    {   
    	$dev_permission = Permission::where('slug','create-tasks')->first();
		$manager_permission = Permission::where('slug', 'edit-users')->first();

		//RoleTableSeeder.php
		$dev_role = new Role();
		$dev_role->slug = 'developer';
		$dev_role->name = 'Front-end Developer';
		$dev_role->save();
		$dev_role->permissions()->attach($dev_permission);

		$manager_role = new Role();
		$manager_role->slug = 'manager';
		$manager_role->name = 'Assistant Manager';
		$manager_role->save();
		$manager_role->permissions()->attach($manager_permission);

		$dev_role = Role::where('slug','developer')->first();
		$manager_role = Role::where('slug', 'manager')->first();

		$createTasks = new Permission();
		$createTasks->slug = 'create-tasks';
		$createTasks->name = 'Create Tasks';
		$createTasks->save();
		$createTasks->roles()->attach($dev_role);

		$editUsers = new Permission();
		$editUsers->slug = 'edit-users';
		$editUsers->name = 'Edit Users';
		$editUsers->save();
		$editUsers->roles()->attach($manager_role);

		$dev_role = Role::where('slug','developer')->first();
		$manager_role = Role::where('slug', 'manager')->first();
		$dev_perm = Permission::where('slug','create-tasks')->first();
		$manager_perm = Permission::where('slug','edit-users')->first();

		$developer = new User();
		$developer->name = 'Mahedi Hasan';
		$developer->email = 'mahedi@gmail.com';
		$developer->password = bcrypt('secrettt');
		$developer->save();
		$developer->roles()->attach($dev_role);
		$developer->permissions()->attach($dev_perm);

		$manager = new User();
		$manager->name = 'Hafizul Islam';
		$manager->email = 'hafiz@gmail.com';
		$manager->password = bcrypt('secrettt');
		$manager->save();
		$manager->roles()->attach($manager_role);
		$manager->permissions()->attach($manager_perm);

		
		return redirect()->back();
    }
}

حالا می تونیم این کار ها رو انجام بدیم :

$user = $request->user();
dd($user->hasRole('developer')); //will return true, if user has role
dd($user->givePermissionsTo('create-tasks'));// will return permission, if not null
dd($user->can('create-tasks')); // will return true, if user has permission

حتی داخل ویو هم میشه استفاده کرد :

@role('developer') 
    Hello developer 
@endrole

10.افزودن Middleware

برای داشتن یک middleware برای چک دسترسی می تونیم این کار رو انجام بدیم :

php artisan make:middleware RoleMiddleware
php artisan make:middleware PermissionMiddleware

App\Http\Middleware\RoleMiddleware.php

namespace App\Http\Middleware;

use Closure;

class RoleMiddleware
{

     public function handle($request, Closure $next, $role)
    {
        
        if(!$request->user()->hasRole($role)) {
            abort(403);
        }

        return $next($request);
    }
}

App\Http\Middleware\PermissionMiddleware.php

namespace App\Http\Middleware;

use Closure;

class PermissionMiddleware
{
    public function handle($request, Closure $next, $permission = null )
    {
        if(!$request->user()->can($permission)) {
            abort(403);
        }
        return $next($request);
    }
}

خب الان Middleware ما آماده استفاده است، پس بهتره داخل لاراول ثبتش کنیم :

App\Http\Kernel.php

protected $routeMiddleware = [
    .
    .
    'role' => \App\Http\Middleware\RoleMiddleware::class,
    'permission' => \App\Http\Middleware\PermissionMiddleware::class,
];

و حالا می تونیم از این Middleware هر جا که خواستیم استفاده کینم :

Route::group(['middleware' => 'role:developer'], function() {

   Route::get('/admin', function() {

      return 'Welcome Admin';
      
   });

});
public function __construct()
{
   $this->middleware('auth'); 
}


public function store(Request $request)
{
    if ($request->user()->can('create-tasks')) {
        //Code goes here
    }
}

public function destroy(Request $request, $id)
{   
    if ($request->user()->can('delete-tasks')) {
      //Code goes here
    }

}

موفق باشید.

 

منبع : CodeChief.org

تگ ها : php web laravel tip snippet tutorial article


نویسنده : سینا | 1399-01-29

نظرات

برای ارسال نظر باید حساب کاربری داشته باشی!

نظری ثبت نشده است.