用户授权
简介
除了提供内置的 authentication (身份验证) 服务外,Laravel 还提供了一种可以很简单就进行使用的方法,来对用户与资源的授权关系进行管理。 它很安全,即使用户已经通过了「身份验证(authentication)」, 用户也可能无权对应用程序中重要的模型或数据库记录进行删除或更改。简单、条理化的系统性,是 Laravel 对授权管理的特性。
Laravel 主要提供了两种授权操作的方法: 拦截器 和 策略。可以把拦截器(gates)和策略(policies)想象成路由和控制器。拦截器(Gates)提供了一种轻便的基于闭包函数的授权方法,像是路由。而策略(policies),就像是一个控制器,对特定模型或资源,进行分组管理的逻辑规则。 在本文档中,我们将首先探讨拦截器(gates),然后研究策略(policies)。
您在构建应用程序时,不用为是仅仅使用拦截器(gates)或是仅仅使用策略(policies)而担心,并不需要在两者中进行唯一选择。大多数的应用程序都同时包含两个方法,并且同时使用两者,能够更好的进行工作。拦截器(gates),更适用于没有与任何模型或资源有关的授权操作,例如查看管理员仪表盘。与之相反,当您希望为特定的模型或资源进行授权管理时,应该使用策略(policies) 方法。
拦截器 (Gates)
编写拦截器(Gates)
注意:通过理解拦截器(Gates),是一个很好的学习 Laravel 授权特性的基础知识的方法。同时,考虑到 Laravel 应用程序的健壮性,应该结合使用策略 (policies) 来组织授权规则。
拦截器(Gates)是用来确定用户是否有权执行给定操作的闭包函数。默认条件下,拦截器(Gates)的使用,是在 App\Providers\AuthServiceProvider
类中的 boot
函数里来规定 Gate
规则。拦截器(Gates)始终接收用户实例为其第一个参数,并且可以选择性的接收其他参数,例如相关的 Eloquent 模型。
在下面的例子中,我们将定义一个拦截器(Gates),并通过调用 App\Models\Post
类,来实现结合用户的 POST 请求,命中给定的规则。拦截器(Gates)将通过比较用户的 id
,和 POST 请求中的 user_id
来实现这个目标:
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
/**
* 注册任何需要身份验证、授权服务的行为
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
}
像是在控制器中操作一样,也可以直接使用类,进行回调数组,完成拦截器(Gates)的定义:
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
/**
* 注册任何需要身份验证、授权服务的行为
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', [PostPolicy::class, 'update']);
}
行为授权控制
如果需要通过拦截器(Gates)来对行为进行授权控制,您可以通过调用 Gate
中的 allows
或 denies
方法。请注意,在使用过程中,您不需要将已经通过身份验证的用户信息传递给这些方法。 Laravel 将会自动把用户信息传递给拦截器(Gates)。以下是一个典型的,在控制器中使用拦截器(Gates)进行行为授权控制的例子:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class PostController extends Controller
{
/**
* 更新给定的帖子
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Post $post
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Post $post)
{
if (! Gate::allows('update-post', $post)) {
abort(403);
}
// 更新帖子...
}
}
如果您需要判断某个用户,是否有权执行某个行为,您可以在 Gate
门面中,使用 forUser
方法:
if (Gate::forUser($user)->allows('update-post', $post)) {
// 这个用户可以提交update...
}
if (Gate::forUser($user)->denies('update-post', $post)) {
// 这个用户不可以提交update...
}
您还可以通过 any
或 none
方法来一次性授权多个行为:
if (Gate::any(['update-post', 'delete-post'], $post)) {
// 用户可以提交update或delete...
}
if (Gate::none(['update-post', 'delete-post'], $post)) {
// 用户不可以提交update和delete...
}
未通过授权时的抛出异常
Illuminate\Auth\Access\AuthorizationException
中准备了 HTTP 的 403 响应。您可以使用 Gate
门面中的 authorize
方法,来规定如果用户进行了未授权的行为时,触发 AuthorizationException
实例 ,该实例会自动转换返回为 HTTP 的 403 响应:
Gate::authorize('update-post', $post);
// 行为已获授权...
上下文的值传递
能够用于拦截器(Gates)的授权方法,(allows
,denies
,check
,any
,none
, authorize
,can
,cannot
) 和在前端进行的授权方法 Blade 指令 (@can
,@cannot
,@canany
) 在第 2 个参数中,可以接收数组。这些数组元素作为参数传递给拦截器(Gates) ,在做出授权决策时可用于其他上下文:
use App\Models\Category;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::define('create-post', function (User $user, Category $category, $pinned) {
if (! $user->canPublishToGroup($category->group)) {
return false;
} elseif ($pinned && ! $user->canPinPosts()) {
return false;
}
return true;
});
if (Gate::check('create-post', [$category, $pinned])) {
// 用户可以请求create...
}