PHP项目Laravel策略授权怎么用

wen PHP项目 3

本文目录导读:

PHP项目Laravel策略授权怎么用

  1. 创建策略
  2. 定义策略方法
  3. 注册策略
  4. 使用策略授权
  5. 在 Blade 模板中使用
  6. 在路由中使用
  7. 在请求验证中使用

Laravel的策略授权(Policy)是用于组织特定模型授权逻辑的类,我来详细讲解如何创建和使用:

创建策略

手动创建

php artisan make:policy PostPolicy

带模型创建(自动生成常用方法)

php artisan make:policy PostPolicy --model=Post

定义策略方法

<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\Response;
class PostPolicy
{
    /**
     * 查看列表
     */
    public function viewAny(User $user)
    {
        return true;
    }
    /**
     * 查看单个文章
     */
    public function view(User $user, Post $post)
    {
        // 公开文章或作者可查看
        if ($post->status === 'published') {
            return true;
        }
        return $user->id === $post->user_id;
    }
    /**
     * 创建文章
     */
    public function create(User $user)
    {
        return $user->role === 'author' || $user->role === 'admin';
    }
    /**
     * 更新文章
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id
            ? Response::allow()
            : Response::deny('您不能编辑别人的文章');
    }
    /**
     * 删除文章
     */
    public function delete(User $user, Post $post)
    {
        // 文章作者或管理员可以删除
        return $user->id === $post->user_id || $user->isAdmin();
    }
    /**
     * 批量删除
     */
    public function deleteAny(User $user)
    {
        return $user->isAdmin();
    }
    /**
     * 恢复软删除的文章
     */
    public function restore(User $user, Post $post)
    {
        return $user->isAdmin();
    }
    /**
     * 永久删除
     */
    public function forceDelete(User $user, Post $post)
    {
        return $user->isAdmin();
    }
}

注册策略

自动发现(推荐)

App\Providers\AuthServiceProvider 中:

<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Models\Post;
use App\Policies\PostPolicy;
class AuthServiceProvider extends ServiceProvider
{
    /**
     * 模型与策略的映射
     */
    protected $policies = [
        Post::class => PostPolicy::class,
        // 其他模型...
    ];
    public function boot()
    {
        $this->registerPolicies();
    }
}

自动发现规则

Laravel 会自动发现策略,只要遵循命名规则:

  • 模型在 App\Models\Post
  • 策略在 App\Policies\PostPolicy

使用策略授权

控制器中使用

<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
    /**
     * 创建文章
     */
    public function store(Request $request)
    {
        // 检查当前用户是否有创建权限
        $this->authorize('create', Post::class);
        // 或者使用 Gate facade
        // if (!Gate::allows('create', Post::class)) {
        //     abort(403);
        // }
        // 创建文章...
    }
    /**
     * 更新文章
     */
    public function update(Request $request, Post $post)
    {
        // 检查当前用户是否有更新该文章的权限
        $this->authorize('update', $post);
        // 更新文章...
    }
    /**
     * 删除文章
     */
    public function destroy(Post $post)
    {
        // 检查删除权限
        $this->authorize('delete', $post);
        $post->delete();
        return redirect()->route('posts.index');
    }
}

在控制器构造函数中自动授权

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

在 Blade 模板中使用

{{-- 使用 can 指令 --}}
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">编辑</a>
@endcan
{{-- 使用 cannot 指令 --}}
@cannot('delete', $post)
    <p>您没有删除权限</p>
@endcan
{{-- 在表单中使用 --}}
@can('create', App\Models\Post::class)
    <a href="{{ route('posts.create') }}">创建文章</a>
@endcan
{{-- 使用授权结果对象 --}}
@can('update', $post)
    <form action="{{ route('posts.update', $post) }}" method="POST">
        @csrf
        @method('PUT')
        <button type="submit">更新</button>
    </form>
@elsecan('delete', $post)
    <p>只有管理员可以删除</p>
@else
    <p>您没有操作权限</p>
@endcan

在路由中使用

Route::middleware('can:update,post')->put('/posts/{post}', [PostController::class, 'update']);
Route::middleware('can:create,App\Models\Post')->post('/posts', [PostController::class, 'store']);

在请求验证中使用

<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdatePostRequest extends FormRequest
{
    public function authorize()
    {
        return $this->user()->can('update', $this->post);
    }
    public function rules()
    {
        return [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ];
    }
}
使用示例(第一段):## 完整实战示例
### 1. 创建迁移和数据模型
```php
// database/migrations/xxx_create_posts_table.php
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->string('title');
    $table->text('content');
    $table->enum('status', ['draft', 'published', 'archived'])->default('draft');
    $table->softDeletes();
    $table->timestamps();
});
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
    use HasFactory, SoftDeletes;
    protected $fillable = ['title', 'content', 'status', 'user_id'];
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\HasMany;
class User extends Authenticatable
{
    use HasFactory;
    protected $fillable = ['name', 'email', 'password', 'role'];
    protected $hidden = ['password', 'remember_token'];
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
    public function isAdmin(): bool
    {
        return $this->role === 'admin';
    }
    public function isEditor(): bool
    {
        return $this->role === 'editor';
    }
}

创建策略类

// app/Policies/PostPolicy.php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\Response;
class PostPolicy
{
    /**
     * 查看文章列表(所有登录用户可以查看)
     */
    public function viewAny(User $user): bool
    {
        return true;
    }
    /**
     * 查看单个文章(公开文章或作者可查看)
     */
    public function view(User $user, Post $post): bool
    {
        if ($post->status === 'published') {
            return true;
        }
        return $user->id === $post->user_id;
    }
    /**
     * 创建文章(作者和管理员可以创建)
     */
    public function create(User $user): bool
    {
        return true; // 所有登录用户都可以创建
    }
    /**
     * 更新文章(作者本人或管理员)
     */
    public function update(User $user, Post $post): Response
    {
        if ($user->id === $post->user_id) {
            return Response::allow('您是这个文章的作者');
        }
        if ($user->isAdmin()) {
            return Response::allow('管理员可以编辑任何文章');
        }
        return Response::deny('您没有权限编辑其他用户发布的文章');
    }
    /**
     * 删除文章(作者本人或管理员)
     */
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id || $user->isAdmin();
    }
    /**
     * 批量删除(仅管理员)
     */
    public function deleteAny(User $user): bool
    {
        return $user->isAdmin();
    }
    /**
     * 恢复被删除的文章(仅管理员)
     */
    public function restore(User $user, Post $post): bool
    {
        return $user->isAdmin();
    }
    /**
     * 永久删除(仅管理员)
     */
    public function forceDelete(User $user, Post $post): bool
    {
        return $user->isAdmin();
    }
    /**
     * 发布文章(作者本人或管理员)
     */
    public function publish(User $user, Post $post): bool
    {
        return $user->id === $post->user_id || $user->isAdmin();
    }
    /**
     * 归档文章(仅管理员)
     */
    public function archive(User $user, Post $post): bool
    {
        return $user->isAdmin();
    }
}

注册策略

// app/Providers/AuthServiceProvider.php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use App\Models\Post;
use App\Policies\PostPolicy;
use App\Models\User;
class AuthServiceProvider extends ServiceProvider
{
    /**
     * 策略映射
     */
    protected $policies = [
        Post::class => PostPolicy::class,
    ];
    public function boot()
    {
        $this->registerPolicies();
        // 定义额外的 Gate(针对不同场景)
        Gate::define('manage-posts', function (User $user) {
            return $user->isAdmin() || $user->isEditor();
        });
    }
}

控制器使用示例

// app/Http/Controllers/PostController.php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class PostController extends Controller
{
    /**
     * 显示文章列表
     */
    public function index()
    {
        $posts = Post::where('status', 'published')->get();
        return view('posts.index', compact('posts'));
    }
    /**
     * 显示创建表单
     */
    public function create()
    {
        $this->authorize('create', Post::class);
        return view('posts.create');
    }
    /**
     * 保存新文章
     */
    public function store(Request $request)
    {
        $this->authorize('create', Post::class);
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ]);
        $post = Auth::user()->posts()->create($validated);
        return redirect()->route('posts.show', $post)->with('success', '文章创建成功!');
    }
    /**
     * 显示单个文章
     */
    public function show(Post $post)
    {
        $this->authorize('view', $post);
        return view('posts.show', compact('post'));
    }
    /**
     * 显示编辑表单
     */
    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);
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            'status' => 'sometimes|in:draft,published,archived',
        ]);
        $post->update($validated);
        return redirect()->route('posts.show', $post)->with('success', '文章更新成功!');
    }
    /**
     * 删除文章
     */
    public function destroy(Post $post)
    {
        $this->authorize('delete', $post);
        $post->delete();
        return redirect()->route('posts.index')->with('success', '文章已删除!');
    }
    /**
     * 发布文章(自定义操作)
     */
    public function publish(Post $post)
    {
        $this->authorize('publish', $post);
        $post->update(['status' => 'published']);
        return back()->with('success', '文章已发布!');
    }
}

视图中的使用

{{-- resources/views/posts/show.blade.php --}}
@extends('layouts.app')
@section('content')
    <div class="container">
        <h1>{{ $post->title }}</h1>
        <p class="text-muted">
            By {{ $post->user->name }} | 
            Status: {{ $post->status }} | 
            Created: {{ $post->created_at->diffForHumans() }}
        </p>
        <div class="mt-4">
            {{ $post->content }}
        </div>
        {{-- 使用 @can 指令判断按钮显示 --}}
        <div class="mt-4">
            {{-- 编辑按钮(当前用户是作者或管理员) --}}
            @can('update', $post)
                <a href="{{ route('posts.edit', $post) }}" class="btn btn-primary">编辑</a>
            @endcan
            {{-- 发布按钮(当前用户是作者或管理员) --}}
            @can('publish', $post)
                @if($post->status === 'draft')
                    <form action="{{ route('posts.publish', $post) }}" method="POST" class="d-inline">
                        @csrf
                        <button type="submit" class="btn btn-success">发布</button>
                    </form>
                @endif
            @endcan
            {{-- 删除按钮 --}}
            @can('delete', $post)
                <form action="{{ route('posts.destroy', $post) }}" method="POST" class="d-inline">
                    @csrf
                    @method('DELETE')
                    <button type="submit" class="btn btn-danger" onclick="return confirm('确定删除吗?')">删除</button>
                </form>
            @endcan
        </div>
        {{-- 错误提示 --}}
        @can('update', $post)
            {{-- 有权限显示的内容 --}}
        @elsecan('publish', $post)
            {{-- 有发布权限的内容 --}}
        @else
            <p class="alert alert-warning">您没有权限编辑这篇文章</p>
        @endcan
    </div>
@endsection

自定义授权消息和响应

// 在策略方法中使用 Response 类自定义错误消息
public function update(User $user, Post $post): Response
{
    if ($user->id === $post->user_id) {
        return Response::allow();
    }
    return Response::denyWithStatus(403, '您不能编辑其他人的文章');
    // 或
    // return Response::deny('自定义错误消息', 403);
}
// 使用 Gate 检查
if (Gate::allows('update', $post)) {
    // 允许
}
if (Gate::denies('update', $post)) {
    // 拒绝
}
// 检查当前用户
if ($request->user()->can('update', $post)) {
    // 允许
}
if ($request->user()->cannot('update', $post)) {
    // 拒绝
}
// 对于管理操作
if ($user->can('update', $post)) {
    // 有更新权限
}
// 抛出自定义错误
if (!$user->can('update', $post)) {
    throw new AuthorizationException('您没有权限执行此操作');
}
// 使用 middleware
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

通过以上示例,你可以看到 Laravel 策略授权提供了灵活且强大的权限控制能力,根据具体需求,你可以自定义更多的授权方法,用于精细化的权限管理。

抱歉,评论功能暂时关闭!