Laravel用户功能

时间:2015-12-19 20:25:00

标签: php laravel laravel-5.1

在Laravel中,您可以轻松地define abilities,然后在用户请求中挂钩,以便执行不同的操作:

$gate->define('update-post', function ($user, $post) {
    return $user->id === $post->user_id;
});

但几乎所有我定义的能力都包含此部分$user->id === $model->user_id。我不喜欢它,因为它是一种重复的条件,我认为它可能更抽象。

我定义的大部分功能都是根据更新/删除记录,所以如果我可以将全局条件应用于所有这些记录,或者如果可以有一个组能力定义哪个类似于我们在路由。

有什么解决方法吗?我真的很喜欢干。

5 个答案:

答案 0 :(得分:6)

Laravel中的所有东西都是可扩展的,这是其服务提供商的力量。

您可以将Gate对象扩展为MyCustomGate对象,并在该对象中执行任何操作。这是一个例子:

<强> MyCustomGate.php

class MyCustomGate extends \Illuminate\Auth\Access\Gate
{
    protected $hasOwnershipVerification = [];

    /**
     * Define a new ability.
     *
     * @param  string  $ability
     * @param  callable|string  $callback
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function defineWithOwnership($ability, $callback, $foreignUserIdKey = "user_id")
    {
        // We will add this 
        $this->hasOwnershipVerification[$ability] = $foreignUserIdKey;

        return $this->define($ability, $callback);
    }

    /**
     * Resolve and call the appropriate authorization callback.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  string  $ability
     * @param  array  $arguments
     * @return bool
     */
    protected function callAuthCallback($user, $ability, array $arguments)
    {
        $callback = $this->resolveAuthCallback(
            $user, $ability, $arguments
        );

        // We will assume that the model is ALWAYS the first key
        $model = is_array($arguments) ? $arguments[0] : $arguments;

        return $this->checkDirectOwnership($ability, $user, $model) && call_user_func_array(
            $callback, array_merge([$user], $arguments)
        );
    }

    /**
     * Check if the user owns a model.
     *
     * @param  string  $ability
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return bool
     */
    protected function checkDirectOwnership($ability, $user, $model)
    {
        if(!isset($this->hasOwnershipVerification[$ability])) {
            return true
        }

        $userIdKey = $this->hasOwnershipVerification[$ability];

        // getAuthIdentifier() is just ->id, but it's better in case the pk of a user is different that id
        return $user->getAuthIdentifier() == $model->{$userIdKey};
    }
}

然后,你必须告诉Laravel使用你的门而不是默认门。您可以在AuthServiceProvider中执行此操作(假设它正在扩展Illuminate\Auth\AuthServiceProvider,只需添加以下方法。

<强> AuthServiceProvider

/**
 * Register the access gate service.
 *
 * @return void
 */
protected function registerAccessGate()
{
    $this->app->singleton(\Illuminate\Contracts\Auth\Access\Gate::class, function ($app) {
        return new MyCustomGate($app, function () use ($app) {
            return $app['auth']->user();
        });
    });
}

这样,您可以使用defineWithOwnership()方法而不是define()来定义异能。您仍然可以将define()用于不需要所有权验证的功能。第三个参数defineWithOwnership()接受$foreignUserIdKey;用于模型具有用户ID的不同字段的情况。

注意:我动态编写了代码并没有尝试,可能有错误,但你明白了。

答案 1 :(得分:5)

我检查了你的问题,但我发现没有“简单”的方法。

相反,我可能会这样做:

<?php


namespace App\Policies;

 use App\User;
 use App\Post;

trait CheckOwnership {
    protected function checkOwnership($user, $model) {
        $owned = $user->id === $model->user_id;
        if ($owned === false)
             throw new NotOwnedException;
    }    
 }

 class PostPolicy
 {

     use CheckOwnership;

    public function update(User $user, Post $post)
    {
         try {
             $this->checkOwnership($user, $post);
             //continue other checks
         } catch (NotOwnedException $ex) {
             return false;
         } 
    }
 }

答案 2 :(得分:4)

将此功能添加到AuthServiceProvider

    public function defineAbilities(array $abilities, $gate)
    {
        foreach($abilities as $name => $model){
            $gate->define($name, function ($user, $model){
                return $user->id === ${$model}->user_id;
            });
        }
    }

然后在内部启动方法

$this->defineAbilities(['ability1' => 'model1', 'ability2' => 'model2'], $gate);

答案 3 :(得分:4)

您可以定义另一个函数并在匿名函数中调用它。这将允许您在一个中心位置使用常用代码,同时仍允许任何特定于资源的逻辑。

将此功能添加到AuthServiceProvider班级:

public function userCheck(User $user, $target)
{
    // do the user id check
    $result = isset($target->user_id) && isset($user) && $user->id === $target->user_id;
    return $result;
}

您修改的代码:

$gate->define('update-post', function ($user, $post) {
    // call the function
    $result = $this->userCheck($user, $post);
    // do some kind of 'update-post' specific check
    return $result/* && some_bool_statement*/;
});

答案 4 :(得分:3)

我认为你可以使用中间件。

只需创建一个管理中间件并在路由和路由组中使用它。

你的项目没有安全漏洞(删除,创建和...动作),因为Laravel有csrf令牌!

您也可以使用before()功能。

然后是一个重要的说明:

如果您没有在Policy类上定义相应的函数并在控制器上调用它$this->authorize($post),则会抛出unauthorized Action错误,除非before()方法return s { {1}}。

例如在true上致电$this->authorize

Dashboard\PostsController

如果我们定义了PostPolicy类:

public function edit($id)
{
    $post = Post::find($id)->first();
    $this->authorize($post);
    return view('dashboard.post')->with(compact('post'));
}

如果用户是管理员,他/她可以编辑帖子,因为我们在class PostPolicy { use HandlesAuthorization; public function before($user, $ability) { return $user->is_admin; } } 方法中return编辑true,尽管没有相同名称的方法(如before()方法在edit)。

事实上,Laravel将在PostsController上的方法mthod之前检查。如果Policy Class的{​​{1}}将在控制器方法上检查具有相同名称的对应方法,并且如果找不到此方法,则用户无法执行操作。

谢谢laravel为我们干!♥