Laravel 权限实现
简介
Laravel提供策略用于为应用程序定义授权逻辑。这些策略确立了管理哪些用户被授权在应用程序模型上执行特定操作的规则。本指南将讨论使用数据库架构、trait和策略在Laravel应用程序中实现细粒度权限所需的步骤。
前提条件
在开始之前,请确保已经设置并运行了Laravel应用程序。您还应该熟悉Laravel迁移、模型和策略。
数据库架构
为在Laravel应用程序中实现权限,请创建包含角色、权限及其关系表的数据库架构。以下示例演示如何使用Laravel迁移创建这些表:
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->id();
$table->string('name');
$table->text('description')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('roles');
}
}
class CreatePermissionsTable extends Migration
{
public function up()
{
Schema::create('permissions', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('permissions');
}
}
class CreateRolePermissionsTable extends Migration
{
public function up()
{
Schema::create('role_permissions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('role_id');
$table->unsignedBigInteger('permission_id');
$table->timestamps();
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
$table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
});
}
public function down()
{
Schema::dropIfExists('role_permissions');
}
}
在此示例中,我们为角色、权限及其关系创建表。roles
表包含ID、名称、描述和时间戳。permissions
表包含ID、名称、描述和时间戳。role_permissions
表包含ID、role_id
、permission_id
和时间戳。role_id
和permission_id
列是外键,分别引用roles
和permissions
表中的id
列。
模型架构
为了在与Role
、Permission
和User
模型交互时提供类型提示并提高代码可读性,我们创建了相应的<Model>Schema
类。这些类定义了代表相应数据库表中列名称的常量。
例如,以下是PermissionSchema
类:
<?php
namespace App\Schemas;
class PermissionSchema
{
const id = 'id';
const name = 'name';
const description = 'description';
const created_at = 'created_at';
const updated_at = 'updated_at';
const deleted_at = 'deleted_at';
}
在上面的例子中,我们为permissions表中的每一列定义常量。然后,我们可以在与Permission
模型及其关联数据交互时使用这些常量来提供类型提示并提高代码可读性。
稍后,我们将在与模型交互时使用这些模式来定义列的名称。例如,在我们稍后将创建的HasPermissions
trait中,我们使用了PermissionSchema::name
常量来引用permissions表中的name
列:
use App\Schemas\PermissionSchema;
// ...
public function hasPermission(string $permission): bool
{
return $this->roles->some(function ($role) use ($permission) {
return $role->permissions->contains(PermissionSchema::name, $permission);
});
}
通过使用PermissionSchema::name
而不是原始字符串"name",我们使代码更具可读性并减少错误发生。此外,如果我们要更改permissions表中name
列的名称,我们可以简单地更新PermissionSchema::name
常量以反映新名称,并且代码中对该常量的所有引用都将自动更新。
通过以这种方式使用模型架构,我们可以使代码随着时间的推移更易于维护和使用。
模型
要在Laravel应用程序中实现细粒度权限,请创建三个模型:Role
、Permission
和User
。这些模型与存储有关角色、权限和用户/角色关系的信息的数据库表交互。
以下是Role
模型的示例:
namespace App\Models;
use App\Schemas\RolePermissionSchema;
use App\Schemas\UserRoleSchema;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Role extends Model
{
use HasFactory;
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, UserRoleSchema::table)->withTimestamps();
}
public function permissions(): BelongsToMany
{
return $this->belongsToMany(Permission::class, RolePermissionSchema::table)->withTimestamps();
}
}
在上面的Role
模型示例中,我们使用BelongsToMany
关系定义Role
和User
模型之间的关系,以及Role
和Permission
模型之间的关系。
以下是Permission
模型的示例:
namespace App\Models;
use App\Schemas\RolePermissionSchema;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Permission extends Model
{
use HasFactory;
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class, RolePermissionSchema::table)->withTimestamps();
}
}
在上面的Permission
模型示例中,我们使用BelongsToMany
关系定义Permission
和Role
模型之间的关系。
最后,以下是User
模型的示例:
namespace App\Models;
use App\Schemas\UserRoleSchema;
use App\Traits\HasPermissions;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasPermissions;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class, UserRoleSchema::table)->withTimestamps();
}
}
在上面的User
模型示例中,我们使用BelongsToMany
关系定义User
和Role
模型之间的关系。此外,我们使用各种trait和属性来处理身份验证、通知和权限。
权限检查Trait
为了简化策略中的权限检查,创建一个为使用它的类添加hasPermission()
方法的trait。以下示例演示如何创建此trait:
<?php
namespace App\Traits;
use App\Schemas\PermissionSchema;
trait HasPermissions
{
public function hasPermission(string $permission): bool
{
return $this->roles->some(function ($role) use ($permission) {
return $role->permissions->contains(PermissionSchema::name, $permission);
});
}
}
此方法简化了检查用户是否具有特定权限的过程。
hasPermission
方法接受一个字符串参数$permission
,并返回一个布尔值。它通过检查与用户关联的所有角色来检查当前用户是否具有指定的权限。
如果没有角色拥有所需的$permission
,some
函数返回false
,表示用户没有必要的权限。
通过将此trait合并到User
模型中,您可以轻松验证用户是否具有特定权限。
使用策略实现细粒度权限
要使用User
模型应用细粒度权限,请为要控制访问权限的每个模型创建策略类。在此示例中,我们将创建一个PostPolicy
来管理对Post
模型的访问。
首先,创建PostPolicy
类:
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
public function viewAny(User $user)
{
return $user->hasPermission('viewAny-posts');
}
public function view(User $user, Post $post)
{
return $user->hasPermission('view-posts') && $post->user_id === $user->id;
}
public function create(User $user)
{
return $user->hasPermission('create-posts');
}
public function update(User $user, Post $post)
{
return $user->hasPermission('update-posts') && $post->user_id === $user->id;
}
public function delete(User $user, Post $post)
{
return $user->hasPermission('delete-posts') && $post->user_id === $user->id;
}
}
请注意,我们利用添加到User
模型的hasPermission
trait。
接下来,在AuthServiceProvider
中注册策略。以下是注册PostPolicy
的示例:
namespace App\Providers;
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Post::class => PostPolicy::class,
];
public function boot()
{
$this->registerPolicies();
}
}
在此示例中,我们为Post
模型注册了PostPolicy
。这指示Laravel使用PostPolicy
来控制对Post
模型实例的访问。
现在,您可以在控制器中使用此策略来授权操作。以下是在控制器中使用PostPolicy
的示例:
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', compact('post'));
}
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// Update the post...
}
}
在此示例中,我们使用authorize()
方法验证经过身份验证的用户是否有权限使用PostPolicy
的update
方法更新给定的帖子。如果用户被授权,我们允许他们编辑或更新帖子。如果没有,我们将返回HTTP 403 Forbidden错误。
通过使用策略来控制对模型的访问,您可以确保只有授权用户才能在应用程序中执行敏感操作。