×
贴子
课程
动态
签到
更新
直播间
系统
碎片
订阅
话题
文档
序言
发布说明
升级指南
贡献指南
开始使用
安装
配置
目录结构
Homestead
Valet
部署
核心架构
请求生命周期
服务容器
服务提供者
Facades
Contracts
基础功能
路由
中间件
CSRF 保护
控制器
请求
响应
视图
URL
表单验证
Session
错误处理
日志
前端开发
Blade 模板
本地化
前端指南
编辑资源 Mix
安全相关
用户认证
用户授权
Email 授权
加密解密
哈希
重置密码
深层发掘
Artisan 命令行
广播系统
缓存系统
集合
事件系统
文件存储
辅助函数
邮件发送
消息通知
扩展包开发
队列
任务调度
数据库
入门
查询构造器
分页
数据库迁移
数据填充
Redis
Eloquent ORM
入门
模型关联
Eloquent 集合
修改器
API 资源
序列化
测试相关
入门指南
HTTP测试
控制台测试
浏览器测试(Laravel Dusk)
数据库测试
测试模拟器
官方扩展包
Cashier
Envoy 部署工具
Horizon
Scout
Laravel 社会化登录
安全相关
Laravel 5.7 中文文档
/
用户授权
# 用户授权 ## 简介 除了提供开箱即用的[身份验证](https://laravel.com/docs/5.7/authentication)服务外,Laravel还提供了一种简单的方法来针对给定资源授权用户操作。与身份验证一样,Laravel的授权方法很简单,授权操作有两种主要方式:门和策略。 想想路线和控制器等大门和政策。盖茨提供了一种简单的基于Closure的授权方法,而策略(如控制器)则围绕特定模型或资源对其逻辑进行分组。我们先探索门,然后检查政策。 在构建应用程序时,您无需在专门使用门或仅使用策略之间进行选择。大多数应用程序很可能包含门和策略的混合,这非常好!盖茨最适用于与任何模型或资源无关的操作,例如查看管理员仪表板。相反,当您希望为特定模型或资源授权操作时,应使用策略。 ## Gates ### 编写Gates Gates 是用来决定用户是否授权执行给予动作的一个闭包函数,并且典型的做法就是在 `App\Providers\AuthServiceProvider` 中使用 `Gate` 来定义. Gates 总是接收一个用户实例作为第一个参数,并且可以接收可选参数,比如相关的 Eloquent 模型: ```php /** * Register any authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Gate::define('update-post', function ($user, $post) { return $user->id == $post->user_id; }); } ``` 也可以使用`Class@method`样式回调字符串定义门,如控制器: ```php /** * Register any authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Gate::define('update-post', 'App\Policies\PostPolicy@update'); } ``` ### 资源 Gates 您还可以使用以下`resource`方法一次定义多个Gate功能: ```php Gate::resource('posts', 'App\Policies\PostPolicy'); ``` 这与手动定义以下Gate定义相同: ```php Gate::define('posts.view', 'App\Policies\PostPolicy@view'); Gate::define('posts.create', 'App\Policies\PostPolicy@create'); Gate::define('posts.update', 'App\Policies\PostPolicy@update'); Gate::define('posts.delete', 'App\Policies\PostPolicy@delete'); ``` 默认情况下,`view`,`create`,`update`,和`delete`能力将被定义。您可以通过将数组作为第三个参数传递给`resource`方法来覆盖默认功能。数组的键定义了能力的名称,而值定义了方法名称。例如,以下代码将仅创建两个新的Gate定义 - `posts.image`和`posts.photo:` ```php Gate::resource('posts', 'PostPolicy', [ 'image' => 'updateImage', 'photo' => 'updatePhoto', ]); ``` ### 授权动作 要使用gate授权操作,您应该使用`allows`或`denies`方法。请注意,您无需将当前经过身份验证的用户传递给这些方法。Laravel将自动将用户传递到关闭门: ```php if (Gate::allows('update-post', $post)) { // The current user can update the post... } if (Gate::denies('update-post', $post)) { // The current user can't update the post... } ``` 如果您想确定特定用户是否有权执行操作,您可以`forUser`在`Gate`外观上使用该方法: ```php if (Gate::forUser($user)->allows('update-post', $post)) { // The user can update the post... } if (Gate::forUser($user)->denies('update-post', $post)) { // The user can't update the post... } ``` ### Gate拦截检查 有时,您可能希望将所有能力授予特定用户。您可以使用该`before`方法定义在所有其他授权检查之前运行的回调: ```php Gate::before(function ($user, $ability) { if ($user->isSuperAdmin()) { return true; } }); ``` 如果`before`回调返回非null结果,则结果将被视为检查结果。 您可以使用该`after`方法定义每次授权检查后要执行的回调。但是,您不能从`after`回调中修改授权检查的结果: ```php Gate::after(function ($user, $ability, $result, $arguments) { // }); ``` ## 创建策略 ### 生成策略 策略是围绕特定模型或资源组织授权逻辑的类。例如,如果您的应用程序是博客,您可能拥有一个`Post`模型和一个相应`PostPolicy`的授权用户操作,例如创建或更新帖子。 您可以使用[artisan命令 `make:policy`](https://laravel.com/docs/5.7/artisan)生成策略。生成的策略将放在`app/Policies`目录中。如果您的应用程序中不存在此目录,Laravel将为您创建它: ```php php artisan make:policy PostPolicy ``` 该命令将生成一个空策略类。如果您希望使用已包含在类中的基本“CRUD”策略方法生成类,则可以在执行`make:policy``--model`命令时指定: ```php php artisan make:policy PostPolicy --model=Post ``` > 所有策略都通过Laravel [服务容器](https://laravel.com/docs/5.7/container)解析,允许您在策略的构造函数中键入提示任何所需的依赖项,以便自动注入它们。 ### 注册策略 一旦策略存在,就需要注册。在`AuthServiceProvider`包括新鲜Laravel应用程序包含一个`policies`,你的雄辩模型映射到其相应的政策属性。注册策略将指示Laravel在授权针对给定模型的操作时使用哪个策略: ```php <?php namespace App\Providers; use App\Post; use App\Policies\PostPolicy; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * The policy mappings for the application. * * @var array */ protected $policies = [ Post::class => PostPolicy::class, ]; /** * Register any application authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); // } } ``` ## 编写策略 ### 策略方法 注册策略后,您可以为其授权的每个操作添加方法。例如,让我们在my `update`上定义一个方法`PostPolicy`,确定给定的是否`User`可以更新给定的`Post`实例。 该`update`方法将接收一个`User`和一个`Post`实例作为其参数,并应返回`true`或`false`指示用户是否有权更新给定的`Post`。因此,对于此示例,让我们验证用户是否`id`匹配`user_id`帖子: ```php <?php namespace App\Policies; use App\User; use App\Post; class PostPolicy { /** * Determine if the given post can be updated by the user. * * @param \App\User $user * @param \App\Post $post * @return bool */ public function update(User $user, Post $post) { return $user->id === $post->user_id; } } ``` 您可以根据需要为其授权的各种操作继续定义策略的其他方法。例如,您可以定义`view`或`delete`授权各种`Post`操作的方法,但请记住,您可以随意为您的策略方法指定任何您喜欢的名称。 ### 不包含模型方法 某些策略方法仅接收当前经过身份验证的用户,而不是他们授权的模型的实例。授权`create`操作时,这种情况最常见。例如,如果您要创建博客,您可能希望检查用户是否有权创建任何帖子。 定义不接收模型实例的策略方法(例如`create`方法)时,它将不会接收模型实例。相反,您应该将方法定义为仅期望经过身份验证的用户: ```php /** * Determine if the given user can create posts. * * @param \App\User $user * @return bool */ public function create(User $user) { // } ``` ### 访客-用户 默认情况下,`false`如果传入的HTTP请求未由经过身份验证的用户启动,则所有门和策略都会自动返回。但是,您可以通过声明“可选”类型提示或`null`为用户参数定义提供默认值来允许这些授权检查传递到您的门和策略: ```php <?php namespace App\Policies; use App\User; use App\Post; class PostPolicy { /** * Determine if the given post can be updated by the user. * * @param \App\User $user * @param \App\Post $post * @return bool */ public function update(?User $user, Post $post) { return $user->id === $post->user_id; } } ``` ### 策略过滤器 对于某些用户,您可能希望授权给定策略中的所有操作。要完成此操作,请`before`在策略上定义方法。该`before`方法将在策略上的任何其他方法之前执行,使您有机会在实际调用预期的策略方法之前授权操作。此功能最常用于授权应用程序管理员执行任何操作: ```php public function before($user, $ability) { if ($user->isSuperAdmin()) { return true; } } ``` > 策略类的 `before` 方法不会被调用,如果该类不包含与被检查的功能名称相符的方法。 ## 使用策略去授权动作 ### 通过用户模型 `User`Laravel应用程序附带的模型包括两种有用的授权操作方法:`can`和`cant`。该`can`方法接收您要授权的操作和相关模型。例如,让我们确定用户是否有权更新给定的`Post`模型: ```php if ($user->can('update', $post)) { // } ``` 如果为给定模型[注册](https://laravel.com/docs/5.7/authorization#registering-policies)了[策略](https://laravel.com/docs/5.7/authorization#registering-policies),则该`can`方法将自动调用相应的策略并返回布尔结果。如果没有为模型注册策略,则该`can`方法将尝试调用与给定操作名称匹配的基于Closure的Gate。 #### 不需要模型的动作 请记住,某些操作`create`可能不需要模型实例。在这些情况下,您可以将类名传递给`can`方法。类名将用于确定授权操作时使用的策略: ```php use App\Post; if ($user->can('create', Post::class)) { // Executes the "create" method on the relevant policy... } ``` ### 通过中间件 Laravel 包含一个可以在请求到达路由或者控制器之前就进行动作授权的中间件。默认情况下, `Illuminate\Auth\Middleware\Authorize` 中间件被指定到你的 `App\Http\Kernel` 类中的m `can` 键上。让我们用一个授权用户更新博客的例子来讲解一下 `can` 这个中间件的使用: ```php use App\Post; Route::put('/post/{post}', function (Post $post) { // 当前用户可以进行更新操作... })->middleware('can:update,post'); ``` 在这个例子中,我们传给了 `can` 中间件两个参数。第一个参数是需要授权的动作名称,第二个参数是我们希望传递给策略方法的路由参数。在这种情况下,我们使用了[隐式模型绑定](https://laravel.com/docs/5.7/routing#implicit-binding)」,一个 `Post` 模型会被传递给策略方法。如果用户不被授权访问指定的动作,这个中间件将会生成带有 `403` 状态码的 HTTP 响应。 #### 不需要指定模型的动作 同样,一些像 `create` 这样的动作可能不需要模型实例。在这种情况下,你可以传一个类名给中间件。当授权这个动作时,这个类名将被用来判断使用哪个策略: ```php Route::post('/post', function () { // 当前用户可以进行创建操作... })->middleware('can:create,App\Post'); ``` ### 通过控制器辅助函数 除了为`User`模型提供有用的`authorize`方法之外,Laravel 也为继承 `App\Http\Controllers\Controller` 这个基类的控制器提供了一个有用的 `authorize` 方法。就像 `can` 方法一样,这个方法需要接收你想授权的动作和相关的模型作为参数。如果这个动作没有被授权, `authorize` 方法会抛出一个 `Illuminate\Auth\Access\AuthorizationException` 的异常,然后 Laravel 默认的异常处理器会将这个异常转化成带有 `403` 状态码的 HTTP 响应。 ```php <?php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller { /** * Update the given blog post. * * @param Request $request * @param Post $post * @return Response * @throws \Illuminate\Auth\Access\AuthorizationException */ public function update(Request $request, Post $post) { $this->authorize('update', $post); // The current user can update the blog post... } } ``` ### 不需要模型的动作 如前所述,某些操作`create`可能不需要模型实例。在这些情况下,您可以将类名传递给`authorize`方法。类名将用于确定授权操作时使用的策略: ```php /** * Create a new blog post. * * @param Request $request * @return Response * @throws \Illuminate\Auth\Access\AuthorizationException */ public function create(Request $request) { $this->authorize('create', Post::class); // The current user can create blog posts... } ``` ### 通过Blade模板 编写刀片模板时,您可能希望仅在用户被授权执行给定操作时才显示页面的一部分。例如,您可能希望仅在用户可以实际更新帖子时显示博客帖子的更新表单。在这种情况下,你可以使用`@can`和`@cannot`家庭指令: ```php @can('update', $post) <!-- The Current User Can Update The Post --> @elsecan('create', App\Post::class) <!-- The Current User Can Create New Post --> @endcan @cannot('update', $post) <!-- The Current User Can't Update The Post --> @elsecannot('create', App\Post::class) <!-- The Current User Can't Create New Post --> @endcannot ``` 这些指令是编写和语句的便捷快捷方式。上述和以上陈述分别转换为以下陈述:`@if``@unless``@can``@cannot` ```php @if (Auth::user()->can('update', $post)) <!-- The Current User Can Update The Post --> @endif @unless (Auth::user()->can('update', $post)) <!-- The Current User Can't Update The Post --> @endunless ``` ### 不需要模型的动作 与大多数其他授权方法一样,如果操作不需要模型实例,则可以将类名传递给`@can`和`@cannot`指令: ```php @can('create', App\Post::class) <!-- The Current User Can Create Posts --> @endcan @cannot('create', App\Post::class) <!-- The Current User Can't Create Posts --> @endcannot ```