授权与权限 #

一、授权概述 #

1.1 什么是授权 #

授权是确定用户是否有权限执行某个操作的过程。

text
认证 vs 授权
├── 认证(Authentication)
│   └── 你是谁?
└── 授权(Authorization)
    └── 你能做什么?

1.2 授权方式 #

text
Laravel授权方式
├── Gates
│   └── 闭包定义权限
├── Policies
│   └── 类定义资源权限
└── 中间件
    └── 路由级别权限检查

二、Gates #

2.1 定义Gate #

php
// app/Providers/AuthServiceProvider.php

public function boot()
{
    $this->registerPolicies();

    // 简单Gate
    Gate::define('access-admin', function ($user) {
        return $user->is_admin;
    });

    // 带参数的Gate
    Gate::define('update-post', function ($user, $post) {
        return $user->id === $post->user_id;
    });

    // 使用类方法
    Gate::define('edit-settings', [SettingsPolicy::class, 'edit']);
}

2.2 检查Gate #

php
// 通过Gate门面
if (Gate::allows('access-admin')) {
    // 允许
}

if (Gate::denies('access-admin')) {
    // 拒绝
}

// 带参数检查
if (Gate::allows('update-post', $post)) {
    // 允许
}

// 检查任意用户权限
if (Gate::forUser($user)->allows('update-post', $post)) {
    // 允许
}

2.3 授权异常 #

php
// 授权失败抛出异常
Gate::authorize('update-post', $post);

// 等同于
if (Gate::denies('update-post', $post)) {
    abort(403);
}

2.4 Blade模板 #

blade
@can('update-post', $post)
    <a href="{{ route('posts.edit', $post) }}">编辑</a>
@endcan

@cannot('update-post', $post)
    <p>无权编辑</p>
@endcannot

@canany(['update-post', 'delete-post'], $post)
    <p>可以编辑或删除</p>
@endcanany

三、Policies #

3.1 创建Policy #

bash
php artisan make:policy PostPolicy

# 同时生成模型关联
php artisan make:policy PostPolicy --model=Post

3.2 定义Policy #

php
// app/Policies/PostPolicy.php
namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    public function viewAny(User $user)
    {
        return true;
    }

    public function view(User $user, Post $post)
    {
        return true;
    }

    public function create(User $user)
    {
        return true;
    }

    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }

    public function restore(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }

    public function forceDelete(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

3.3 注册Policy #

php
// app/Providers/AuthServiceProvider.php

protected $policies = [
    Post::class => PostPolicy::class,
];

3.4 使用Policy #

php
// 控制器中
public function update(Request $request, Post $post)
{
    $this->authorize('update', $post);
    
    // 或
    if ($request->user()->cannot('update', $post)) {
        abort(403);
    }
    
    // 更新文章
}

// 不需要传递模型的方法
public function create(Request $request)
{
    $this->authorize('create', Post::class);
    
    // 创建文章
}

3.5 资源控制器授权 #

php
class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }
}

四、中间件授权 #

4.1 can中间件 #

php
// 路由中使用
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

// 控制器构造函数
public function __construct()
{
    $this->middleware('can:update,post')->only('update');
}

五、角色和权限 #

5.1 数据库设计 #

php
// 用户表添加角色字段
Schema::table('users', function (Blueprint $table) {
    $table->string('role')->default('user');
});

// 或使用多对多关系
Schema::create('roles', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->timestamps();
});

Schema::create('permissions', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->timestamps();
});

Schema::create('role_user', function (Blueprint $table) {
    $table->foreignId('role_id')->constrained();
    $table->foreignId('user_id')->constrained();
});

Schema::create('permission_role', function (Blueprint $table) {
    $table->foreignId('permission_id')->constrained();
    $table->foreignId('role_id')->constrained();
});

5.2 用户模型 #

php
class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }

    public function permissions()
    {
        return $this->belongsToMany(Permission::class);
    }

    public function hasRole($role)
    {
        if (is_string($role)) {
            return $this->roles->contains('name', $role);
        }
        
        return $role->intersect($this->roles)->isNotEmpty();
    }

    public function hasPermission($permission)
    {
        if (is_string($permission)) {
            return $this->permissions->contains('name', $permission);
        }
        
        return $permission->intersect($this->permissions)->isNotEmpty();
    }

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

5.3 定义角色Gate #

php
// AuthServiceProvider.php
public function boot()
{
    $this->registerPolicies();

    Gate::before(function ($user, $ability) {
        if ($user->hasRole('admin')) {
            return true;
        }
    });

    Gate::define('edit-posts', function ($user) {
        return $user->hasPermission('edit-posts');
    });
}

六、策略响应 #

6.1 自定义响应 #

php
class PostPolicy
{
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id
            ? Response::allow()
            : Response::deny('您无权编辑此文章');
    }
}

6.2 捕获响应消息 #

php
public function update(Request $request, Post $post)
{
    $response = Gate::inspect('update', $post);

    if ($response->allowed()) {
        // 允许
    } else {
        echo $response->message();
    }
}

七、策略发现 #

7.1 自动发现 #

Laravel可以自动发现策略:

php
// app/Providers/AuthServiceProvider.php

protected $policies = [
    // 手动注册
];

// 自动发现
public function boot()
{
    Gate::guessPolicyNamesUsing(function ($modelClass) {
        return 'App\\Policies\\' . class_basename($modelClass) . 'Policy';
    });
}

八、完整示例 #

8.1 文章授权 #

php
// PostPolicy.php
class PostPolicy
{
    public function viewAny(User $user)
    {
        return true;
    }

    public function view(User $user, Post $post)
    {
        return $post->published || $user->id === $post->user_id;
    }

    public function create(User $user)
    {
        return $user->hasVerifiedEmail();
    }

    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id || $user->hasRole('editor');
    }

    public function delete(User $user, Post $post)
    {
        return $user->id === $post->user_id || $user->hasRole('admin');
    }

    public function publish(User $user, Post $post)
    {
        return $user->hasRole('editor') || $user->hasRole('admin');
    }
}

8.2 控制器使用 #

php
class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }

    public function index()
    {
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }

    public function create()
    {
        return view('posts.create');
    }

    public function store(Request $request)
    {
        $post = Post::create($request->validated());
        return redirect()->route('posts.show', $post);
    }

    public function edit(Post $post)
    {
        return view('posts.edit', compact('post'));
    }

    public function update(Request $request, Post $post)
    {
        $post->update($request->validated());
        return redirect()->route('posts.show', $post);
    }

    public function destroy(Post $post)
    {
        $post->delete();
        return redirect()->route('posts.index');
    }
}

8.3 视图使用 #

blade
@foreach($posts as $post)
    <div class="post">
        <h2>{{ $post->title }}</h2>
        
        @can('update', $post)
            <a href="{{ route('posts.edit', $post) }}">编辑</a>
        @endcan
        
        @can('delete', $post)
            <form action="{{ route('posts.destroy', $post) }}" method="POST">
                @csrf
                @method('DELETE')
                <button type="submit">删除</button>
            </form>
        @endcan
    </div>
@endforeach

九、总结 #

9.1 核心概念 #

概念 说明
Gate 闭包定义权限
Policy 类定义资源权限
authorize() 授权检查方法
@can Blade授权指令
can中间件 路由授权中间件

9.2 下一步 #

掌握了授权系统后,让我们继续学习 RESTful API,了解Laravel API开发!

最后更新:2026-03-28