
تعریف سطح دسترسی در لاراول
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
}
}
موفق باشید.
نکته:
گاهی اوقات بخاطر کش کردن تنظیمات، بخش Blade directives کار نمیکنن، برای همین باید دستورات زیر رو اجرا کنید:
php artisan cache:clear
php artisan config:clear
php artisan view:clear
منبع : CodeChief.org
دسته بندی