Laravel 5中的中间件“Owner”是什么?

4
我在创建“owner”中间件时遇到了问题。
例如,我有一个与user_id键相关联的ArticlesUser模型。
我想将“owner”中间件添加到ArticlesController中,以便只有该文章的所有者可以编辑、更新和删除它。
我已经搜索了一段时间,但没有找到可行的代码。其中一些尝试使用表单请求使其工作,但我更倾向于使用中间件。
2个回答

9
  1. 创建中间件:
php artisan make:middleware OwnerMiddleware

namespace App\Http\Middleware;

use App\Article;
use Closure;
use Illuminate\Contracts\Auth\Guard;

class OwnerMiddleware
{
    /**
     * The Guard implementation.
     *
     * @var Guard
     */
    protected $auth;

    /**
     * Create a new filter instance.
     *
     * @param  Guard  $auth
     * @return void
     */
    public function __construct(Guard $auth)
    {
        $this->auth = $auth;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $articleId = $request->segments()[1];
        $article = Article::findOrFail($articleId);

        if ($article->user_id !== $this->auth->getUser()->id) {
            abort(403, 'Unauthorized action.');
        }

        return $next($request);
    }
}

将其添加到app\Http\Kernel.php中:
protected $routeMiddleware = [
    'owner' => 'App\Http\Middleware\OwnerMiddleware',
];

在您的路由中使用中间件:
Route::group(['middleware' => ['owner']], function() {
    // your route
});

每个模型都需要创建一个新的handle()吗? - Jack Barham
这看起来还行,但是它依赖于URL结构,对吗?有没有办法只需获取$request中的模型并比较user_id呢? - stueynet

5

或者您可以使用路由和中间件参数,它具有以下优点:

  • 即使请求结构发生更改,您的中间件仍将起作用
  • 中间件可重复使用于不同资源
  • 您可以在控制器内部使用它

这是中间件(app/Http/Middleware/AbortIfNotOwner.php):

<?php

namespace App\Http\Middleware;

use Closure;

class AbortIfNotOwner
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string    $resourceName
     * @return mixed
     */
    public function handle($request, Closure $next, $resourceName)
    {
        $resourceId = $request->route()->parameter($resourceName);

        $user_id = \DB::table($resourceName)->find($resourceId)->user_id;

        if ($request->user()->id != $user_id) {
            abort(403, 'Unauthorized action.');
        }

        return $next($request);
    }
}

app\Http\Kernel.php 中:

protected $routeMiddleware = [
     'owner' => 'App\Http\Middleware\AbortIfNotOwner',
];

在您的路由文件中 (app/Http/routes.php):

Route::group(['middleware' => ['owner:articles']], function() {
    // your route
});

并且可以选择在控制器中调用:

public function __construct()
{
    $this->middleware('owner:articles', ['only' => ['edit', 'update']]);
}

非常聪明!这应该是 Laravel 的默认设置。 - dynamic
问题是它最终会查询两次,你最好在模型绑定之后或在更新方法中的模型上实现它。 - malhal
你是什么意思?“构造”和“路由”方法是互补的,你不需要同时使用。 - Gluten
你正在每次网络请求时从数据库中获取所有者,这会导致不必要的n+1错误。从请求对象中获取经过身份验证的用户,中间件已经在传递这些数据了,没有必要为每个请求额外访问数据库。 - Jeremy Anderson
问题在于\DB::table($resourceName)->find($resourceId)->user_id,它查询模型,检索它(连同所有不必要包含的列),假设user_id存在于模型上(它可能有一个违反约定的名称),然后只是丢弃模型...当请求到达控制器时,控制器本身必须重新获取模型,这需要两个查询,而只需要一个就足够了。 - Deji
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接