跳转到主要内容

认证与授权的区别

认证(Authentication) 用来确认用户「是谁」。登录处理就是典型例子。 授权(Authorization) 用来判断该用户「能做什么」。例如只能编辑自己发的帖子、只有管理员能修改设置等。 在通过认证实现登录功能后,下一步就是授权。
Laravel 的授权功能提供 Gate 与 Policy 两种方式。选择哪种取决于用途。
选择依据
场景推荐
与具体模型无关的简单判断Gate
针对模型的 CRUD 权限管理Policy
管理后台访问控制Gate
博客文章的发布、编辑、删除权限Policy

Gate

Gate 是基于闭包的简单授权检查,适合与特定模型无关的权限判断。

Gate 的授权流程

定义 Gate

Gate 定义在 App\Providers\AppServiceProviderboot 方法中,使用 Gate::define()
// app/Providers/AppServiceProvider.php

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
Gate 闭包的第一个参数总是当前已认证用户。第二个及以后的参数可以传入目标模型等额外信息。

使用 Gate 检查权限

在控制器中使用 Gate 检查权限可用 Gate::allows()Gate::denies()
use Illuminate\Support\Facades\Gate;

public function update(Request $request, Post $post): RedirectResponse
{
    if (! Gate::allows('update-post', $post)) {
        abort(403);
    }

    // 更新文章的处理...

    return redirect('/posts');
}

使用 Gate::authorize() 抛出异常

无权限时自动返回 403 响应可以使用 Gate::authorize(),省去手写 abort(403)
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);

    // 有权限时到达此处
    // 更新文章的处理...

    return redirect('/posts');
}
当权限不足时,Gate::authorize() 会抛出 Illuminate\Auth\Access\AuthorizationException。Laravel 会自动将其转换为 403 响应。

管理员绕过(before 方法)

若想给管理员用户所有权限,可以使用 Gate::before()
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    // $ability 是正在尝试的 gate 名(例如 'update-post')
    Gate::before(function (User $user, string $ability) {
        if ($user->isAdministrator()) {
            return true;
        }
    });

    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
before 闭包返回非 null 值,将其作为最终结果。返回 null 或无返回值时会继续执行正常的 Gate 判定。

Blade 中的 @can / @cannot

在模板中使用 Gate,可用 @can@cannot 指令:
@can('update-post', $post)
    <a href="{{ route('posts.edit', $post) }}">编辑</a>
@endcan

@cannot('update-post', $post)
    <p>你无权编辑该文章。</p>
@endcannot

Policy

Policy 将某模型相关的授权逻辑归入一个类。对 Post 模型的创建、查看、编辑、删除等权限,用 Policy 比 Gate 更合适。

Policy 的授权流程

生成 Policy

使用 make:policy Artisan 命令生成 Policy 类。
php artisan make:policy PostPolicy
若想生成包含对应模型 CRUD 方法的完整骨架,使用 --model
php artisan make:policy PostPolicy --model=Post
会生成 app/Policies/PostPolicy.php

模型与 Policy 的自动识别

Laravel 默认根据命名约定自动识别 Policy:
  • 模型:app/Models/Post.php
  • Policy:app/Policies/PostPolicy.php
只要遵循命名约定,就无需手动注册。
若不遵循命名约定或希望手动注册,可在 AppServiceProviderboot 中使用 Gate::policy()
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;

Gate::policy(Post::class, PostPolicy::class);
在 Laravel 13 中,也可以通过 #[UsePolicy] 属性在模型上进行声明式注册。
use App\Policies\PostPolicy;
use Illuminate\Database\Eloquent\Attributes\UsePolicy;

#[UsePolicy(PostPolicy::class)]
class Post extends Model {}

实现 Policy 方法

通过 --model 生成的 Policy 已经带有标准 CRUD 方法。
<?php

namespace App\Policies;

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

class PostPolicy
{
    /** 是否可查看列表 */
    public function viewAny(User $user): bool
    {
        return true;
    }

    /** 是否可查看某一条 */
    public function view(User $user, Post $post): bool
    {
        return true;
    }

    /** 是否可创建 */
    public function create(User $user): bool
    {
        return true;
    }

    /** 是否可更新 */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    /** 是否可删除 */
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

管理员绕过(before 方法)

Policy 中也可通过 before 给管理员所有权限:
/**
 * 在其他 policy 方法之前调用
 * $ability 为 policy 方法名('update'、'delete' 等)
 */
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true;
    }

    return null; // 返回 null 则继续正常 policy 判断
}
before 只会在 policy 类中存在对应方法时才会被调用。例如 update 方法不存在,before 就不会被调用。

before 回调的执行顺序

在控制器中使用 Policy

authorize() 方法

Laravel 控制器提供了 authorize() 帮助方法(若未使用基础控制器,可使用 Gate::authorize())。
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    public function update(Request $request, Post $post): RedirectResponse
    {
        Gate::authorize('update', $post);

        // 更新文章的处理...

        return redirect()->route('posts.index');
    }

    public function store(Request $request): RedirectResponse
    {
        Gate::authorize('create', Post::class);

        // 创建文章的处理...

        return redirect()->route('posts.index');
    }

    public function destroy(Post $post): RedirectResponse
    {
        Gate::authorize('delete', $post);

        $post->delete();

        return redirect()->route('posts.index');
    }
}

使用 authorizeResource() 一次注册 RESTful Policy

在构造器中调用 authorizeResource(),可以将控制器各个 action 自动映射到对应 policy 方法。
<?php

namespace App\Http\Controllers;

use App\Models\Post;

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

    // index()、show()、create()、store()、edit()、update()、destroy() 会自动应用对应 policy 方法
}
action 与 policy 方法的对应关系:
控制器 actionpolicy 方法
indexviewAny
showview
create / storecreate
edit / updateupdate
destroydelete
使用 authorizeResource() 可以省去在每个 action 单独调用 authorize()。与 RESTful 资源控制器配合尤其方便。

在中间件中授权

在路由级做授权检查,使用 can 中间件。
use App\Models\Post;

// 只允许具备更新权限的用户访问
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

// 只允许具备创建权限的用户访问
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('can:create,App\Models\Post');
也可以用 can 方法写得更简洁:
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->can('update', 'post');

在 Blade 中使用 Policy

注册好 Policy 之后,Blade 的 @can / @cannot 指令也会自动使用 Policy。
{{-- 只对有权限的用户显示编辑按钮 --}}
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}" class="btn">编辑</a>
@endcan

{{-- 删除按钮同理 --}}
@can('delete', $post)
    <form method="POST" action="{{ route('posts.destroy', $post) }}">
        @csrf
        @method('DELETE')
        <button type="submit">删除</button>
    </form>
@endcan

{{-- 新建按钮 --}}
@can('create', App\Models\Post::class)
    <a href="{{ route('posts.create') }}">新建文章</a>
@endcan

实用示例:博客文章权限管理

在博客应用中实现「作者才能编辑或删除自己的文章」:
1

生成 Policy

php artisan make:policy PostPolicy --model=Post
2

实现 Policy 方法

<?php

namespace App\Policies;

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

class PostPolicy
{
    /** 给管理员所有权限 */
    public function before(User $user, string $ability): bool|null
    {
        if ($user->is_admin) {
            return true;
        }

        return null;
    }

    public function viewAny(User $user): bool
    {
        return true;
    }

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

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

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

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

在控制器中添加 authorizeResource()

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

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

    public function index(): View
    {
        $posts = Post::latest()->paginate(10);
        return view('posts.index', compact('posts'));
    }

    public function store(Request $request): RedirectResponse
    {
        $post = $request->user()->posts()->create(
            $request->validate([
                'title' => ['required', 'string', 'max:255'],
                'body'  => ['required', 'string'],
            ])
        );

        return redirect()->route('posts.show', $post);
    }

    public function update(Request $request, Post $post): RedirectResponse
    {
        $post->update(
            $request->validate([
                'title' => ['required', 'string', 'max:255'],
                'body'  => ['required', 'string'],
            ])
        );

        return redirect()->route('posts.show', $post);
    }

    public function destroy(Post $post): RedirectResponse
    {
        $post->delete();

        return redirect()->route('posts.index');
    }
}
4

在 Blade 模板中添加权限判断

{{-- resources/views/posts/show.blade.php --}}
<h1>{{ $post->title }}</h1>
<p>{{ $post->body }}</p>
<p>作者:{{ $post->user->name }}</p>

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

@can('delete', $post)
    <form method="POST" action="{{ route('posts.destroy', $post) }}">
        @csrf
        @method('DELETE')
        <button type="submit">删除</button>
    </form>
@endcan

小结

  • Gate:与特定模型无关的简单权限判断。例如管理后台访问、全局设置修改权限等。
  • Policy:针对模型的 CRUD 权限。为 PostOrderComment 等资源分别建立 policy 类。
// 通过 Gate 判定
Gate::allows('update-post', $post);      // true/false
Gate::denies('update-post', $post);      // true/false
Gate::authorize('update-post', $post);   // 失败时抛异常

// 通过用户模型判定
$user->can('update', $post);     // true/false
$user->cannot('update', $post);  // true/false

// 控制器中
Gate::authorize('update', $post);        // 失败时 403

// Blade 中
@can('update', $post) ... @endcan
@cannot('update', $post) ... @endcannot
# 生成空 policy
php artisan make:policy PostPolicy

# 生成与模型对应的 CRUD 方法的 policy
php artisan make:policy PostPolicy --model=Post
最后修改于 2026年7月13日