PHP项目Laravel表单请求授权检查

wen PHP项目 5

本文目录导读:

PHP项目Laravel表单请求授权检查

  1. 创建Form Request类
  2. 基本授权检查
  3. 在控制器中使用
  4. 高级授权模式
  5. 自定义授权失败响应
  6. 路由模型绑定结合
  7. 使用中间件进行基础授权
  8. 前端错误处理
  9. 测试授权
  10. 最佳实践建议

在Laravel中,表单请求授权检查主要通过Form Request类来实现,以下是详细的实现方式:

创建Form Request类

php artisan make:request StorePostRequest

基本授权检查

<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
    /**
     * 确定用户是否有权执行此请求
     */
    public function authorize(): bool
    {
        // 方式1:返回true表示允许所有用户
        return true;
        // 方式2:基于认证用户
        return auth()->check();
        // 方式3:基于用户角色
        return auth()->user()->hasRole('admin');
        // 方式4:基于模型所有权
        $post = $this->route('post');
        return $post && $this->user()->can('update', $post);
    }
    /**
     * 获取应用于请求的验证规则
     */
    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            'category_id' => 'required|exists:categories,id',
        ];
    }
    /**
     * 配置验证器实例
     */
    public function withValidator($validator)
    {
        $validator->after(function ($validator) {
            if ($this->input('title') === 'forbidden') {
                $validator->errors()->add('title', '该标题不允许使用');
            }
        });
    }
    /**
     * 自定义错误消息
     */
    public function messages(): array
    {
        return [
            'title.required' => '标题不能为空',
            'title.max' => '标题不能超过255个字符',
            'content.required' => '内容不能为空',
        ];
    }
    /**
     * 自定义属性名称
     */
    public function attributes(): array
    {
        return [
            'title' => '文章标题',
            'content' => '文章内容',
        ];
    }
}

在控制器中使用

<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Models\Post;
class PostController extends Controller
{
    public function store(StorePostRequest $request)
    {
        // 请求已经通过授权和验证
        $validated = $request->validated();
        $post = Post::create($validated);
        return redirect()->route('posts.show', $post);
    }
    public function update(UpdatePostRequest $request, Post $post)
    {
        // 如果授权失败,会自动返回403或重定向
        $post->update($request->validated());
        return redirect()->route('posts.show', $post);
    }
}

高级授权模式

基于策略(Policy)

// 创建UpdatePostRequest
class UpdatePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        $post = $this->route('post');
        // 使用Policy进行授权
        return $this->user()->can('update', $post);
    }
    public function rules(): array
    {
        return [
            'title' => 'sometimes|string|max:255',
            'content' => 'sometimes|string',
        ];
    }
}

基于Gate

class UpdatePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        $post = $this->route('post');
        // 使用Gate
        return Gate::allows('update-post', $post);
    }
}

自定义授权失败响应

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()->isSubscribed();
    }
    /**
     * 授权失败的自定义响应
     */
    protected function failedAuthorization()
    {
        if ($this->expectsJson()) {
            throw new HttpResponseException(
                response()->json([
                    'message' => '您没有权限执行此操作',
                    'status' => 403
                ], 403)
            );
        }
        abort(403, '您没有权限执行此操作');
    }
}

路由模型绑定结合

// 路由定义
Route::put('/posts/{post}', [PostController::class, 'update'])->name('posts.update');
// FormRequest中使用
class UpdatePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        // 通过route方法获取模型实例
        $post = $this->route('post');
        // 检查用户是否是该文章的创建者
        return $post->user_id === $this->user()->id;
    }
    public function rules(): array
    {
        // 可以基于模型状态动态生成规则
        $post = $this->route('post');
        $rules = [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ];
        if ($post->status === 'published') {
            $rules['title'] .= '|unique:posts,title,' . $post->id;
        }
        return $rules;
    }
}

使用中间件进行基础授权

// 在控制器中使用中间件
class PostController extends Controller
{
    public function __construct()
    {
        // 只有认证用户可以访问
        $this->middleware('auth');
        // 只有admin角色可以访问
        $this->middleware('role:admin')->only('destroy');
        // 验证post所有权
        $this->middleware('can:update,post')->only(['update', 'edit']);
    }
}

前端错误处理

{{-- Blade模板示例 --}}
@if($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
{{-- 单个字段错误 --}}
<input type="text" name="title" value="{{ old('title') }}">
@error('title')
    <span class="text-danger">{{ $message }}</span>
@enderror

测试授权

class PostRequestTest extends TestCase
{
    public function test_user_can_create_post()
    {
        $user = User::factory()->create();
        $response = $this->actingAs($user)
            ->post('/posts', [
                'title' => 'Test Post',
                'content' => 'Post content'
            ]);
        $response->assertSessionHasNoErrors();
        $response->assertRedirect();
    }
    public function test_unauthorized_user_cannot_create_post()
    {
        $response = $this->post('/posts', [
            'title' => 'Test Post',
            'content' => 'Post content'
        ]);
        $response->assertForbidden();
    }
}

最佳实践建议

  1. 单一职责:每个FormRequest只处理一个请求场景
  2. 授权与验证分离authorize()只负责权限,rules()只负责验证
  3. 复用性:将通用的授权逻辑提取到Policy或Gate中
  4. 错误处理:提供友好的中文错误消息
  5. 安全考虑:永远不要信任客户端数据,始终服务端验证

这样配置后,Laravel会自动处理授权失败的情况,返回403错误和验证失败消息。

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