在Laravel中,您可以轻松地define abilities,然后在用户请求中挂钩,以便执行不同的操作:
$gate->define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
但几乎所有我定义的能力都包含此部分$user->id === $model->user_id
。我不喜欢它,因为它是一种重复的条件,我认为它可能更抽象。
我定义的大部分功能都是根据更新/删除记录,所以如果我可以将全局条件应用于所有这些记录,或者如果可以有一个组能力定义哪个类似于我们在路由。
有什么解决方法吗?我真的很喜欢干。
答案 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为我们干!♥