Laravel 5.1 中的ACL用户授权及权限检查功能例子

1、引言
Laravel提供的开箱即用的认证功能使得用户注册、登录、退出和密码重置变得便捷和简单。

但是如果你需要控制访问站点特定部分,或者让非管理员打开/关闭特定页面,又或者确保某些用户只能编辑自己发布的东西(如文章),那么 你就需要引入类似BeatSwitch Lock这样的工具或者自己手动编写这样的功能。我们将这样的功能称之为ACL:Access Control Lists(访问控制列表),用于定义用户基于其用户记录属性操作或查看特定事物的权限。

幸运的是,从Laravel 5.1.11开始,Laravel提供了开箱即用的授权功能用于实现上述需求,我们不再需要做任何额外工作,用就是了。

注:在开始本节之前,请参考升级指南将Laravel升级到Laravel 5.1.11,否则不能实现相关功能。
2、能做什么?
Laravel提供的开箱即用的ACL被称作Gate(这并不是一个类似Spark的产品名称,而只是一个类和门面的名称)。

使用Gate类(注入或使用Gate门面)允许我们轻松检查某个用户(当前登录用户或指定用户)是否允许操作特定事物。检查代码如下:

if (Gate::denies('update-post', $post)) {
    abort(403);
}
将这段代码放到控制器中,它将会使用定义好的规则update-post来检查当前认证用户是否有权限更新指定文章。

你还可以使用Gate::allows,该方法与denies方法相对,还可以在Blade视图模板中通过@can来使用,还有更多更多,让我们接下来一窥究竟。

3、如果使用?
Laravel ACL建立在“权限”概念之上,权限包括一个键(例如update-post)和一个返回true或false的闭包(可传入参数)。

3.1 定义权限

让我们在AuthServiceProvider中定义用户更新文章权限update-post如下:

<?php

namespace App\Providers;

use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider{
    /**
     * 注册应用所有的认证/授权服务.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        parent::registerPolicies($gate);

        $gate->define('update-post', function ($user, $post) {
            return $user->id === $post->user_id;
        });
    }
}
正如你所看到的,定义权限闭包的第一个参数是指定用户,如果当前用户没有通过登录认证,Gate将会自定返回false。

当然,除了闭包之外,还可以通过类方法作为第二个参数来替代闭包,该类会在容器中解析:

$gate->define('update-post', 'PostPolicy@update');
3.2 通过Gate门面检查权限

Gate提供如下方法进行权限检查:check、allows和denies,check和allows功能用法完全一样,而denies和allows功能相反。

如果你使用门面检查权限,则不需要传递用户实例,Gate门面会自动将当前用户传递进来:

if (Gate::denies('update-post', $post)) {      
    abort(403);
}
如果你在权限中定义了多个参数:

Gate::define('delete-comment', function ($user, $post, $comment) {
    //
});
则检查方法如下:

if (Gate::allows('delete-comment', [$post, $comment])) {
    //
}
如果你想检查非当前认证用户是否有权限操作,调用方法如下:

if (Gate::forUser($user)->allows('update-post', $post)) {
    //
}
3.3 使用Gate注入检查权限

和以往一样,可以注入Gate类而不是使用其门面,注入的类和你在AuthServiceProvider中一样——Illuminate\Contracts\Auth\Access\Gate:

public function somethingResolvedFromContainer(Gate $gate)
{
    if ($gate->denies('update-post')) {
        // etc.
    }
}
3.4 使用User模型检查权限

Laravel的App\User模型现在使用了Authorizabletrait,因此可以使用其提供的can和cannot方法,分别对应Gate的allows和denies方法。

所以我们也可以使用User模型来检查权限:

public function update(Request $request, $id)
{
    $post = Post::findOrFail($id);

    if ($request->user()->cannot('update-post', $post)) {
        abort(403);
    }

    // 更新文章...
}
3.5 在Blade中检查权限

你可以在Blade中使用@can指令来检查权限:

<a href="/post/{{ $post->id }}">查看文章</a>

@can('update-post', $post)
    <a href="/post/{{ $post->id }}/edit">编辑文章</a>
@endcan
与之相对的是@else指令:

@can('update-post', $post)
    <!-- The Current User Can Update The Post -->
@else
    <!-- The Current User Can't Update The Post -->
@endcan
3.6 中止权限检查

如果是管理员或超级用户拥有所有权限怎么做?或者你想要为用户临时切换ACL逻辑又该当如何?

Gate提供的before方法允许你在一些特定情况下在执行其他检查前就返回,不再往下检查权限:

$gate->before(function ($user, $ability) {
    if ($user->last_name === 'Stauffer') {
        return true;
    }
});
或者使用用户自己的时候:

$gate->before(function ($user, $ability) {
    if ($user->isOwner()) {
        return true;
    }
});
3.7 策略类

随着应用逻辑越来越复杂,要处理的权限越来越多,将所有权限定义在AuthServiceProvider显然不是一个明智的做法,因此Laravel引入了策略类,策略类是一些原生的PHP类,和控制器基于资源对路由进行分组类似,策略类基于资源对权限进行分组管理。

生成策略类

可以使用如下Artisan命令生成PostPolicy策略类:

php artisan make:policy PostPolicy
生成的策略类位于app/Policies目录。

然后我们可以在AuthServiceProvider的policies属性中注册策略类:

protected $policies = [
    Post::class => PostPolicy::class,
];
下面我们编辑PostPolicy如下:

<?php

namespace App\Policies;

use App\User;
use App\Post;

class PostPolicy{
    /**
     * 判断给定文章是否可以被给定用户更新
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return bool
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}
注:所有策略类都通过服务容器进行解析,这意味着你可以在策略类的构造函数中类型提示任何依赖,它们将会自动被注入。
检查策略

如果为某个资源类型定义了策略类,Gate将会使用第一个参数来判断检查策略类上的哪个方法。

因此,要检查是否有权限更新某篇文章,只需要传入文章实例和update权限:

<?php

namespace App\Http\Controllers;

use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;

class PostController extends Controller{
    /**
     * 更新给定文章
     *
     * @param  int  $id
     * @return Response
     */
    public function update($id)
    {
        $post = Post::findOrFail($id);

        if (Gate::denies('update', $post)) {
            abort(403);
        }

        // 更新文章...
    }
}
当然也可以使用User模型和Blade指令检查权限。

此外,Laravel还提供了一个全局帮助函数policy来检查权限:

if (policy($post)->update($user, $post)) {
    //
}
3.8 控制器授权

由于大多数授权都会在检查权限失败的情况下退出控制器方法,因此在控制器中检查权限有一条捷径(AuthorizesRequeststrait提供,该trait在基类控制器Controller中被使用):

<?php

namespace App\Http\Controllers;

use App\Post;
use App\Http\Controllers\Controller;

class PostController extends Controller{
    /**
     * 更新给定文章
     *
     * @param  int  $id
     * @return Response
     */
    public function update($id)
    {
        $post = Post::findOrFail($id);
        $this->authorize('update', $post);
        // 更新文章...
    }
}
和我们上面的例子一样,如果授权失败会抛出403错误。

最后,如果你的控制器方法名和策略类中的方法名相同,例如都是update,则可以省略authorize的第一个参数:

public function update($id){
    $post = Post::findOrFail($id);
    $this->authorize($post);
    // 更新文章...
}
此外,AuthorizesRequests也提供了对非当前认证用户权限检查的支持:

$this->authorizeForUser($user, 'update', $post);

时间: 2025-01-18 12:46:28

Laravel 5.1 中的ACL用户授权及权限检查功能例子的相关文章

android-Android 代码中怎么判断用户是否禁用权限

问题描述 Android 代码中怎么判断用户是否禁用权限 我要实现一个打电话功能,点击按钮跳到打电话界面,在跳转之前,怎么判断用户是否禁用了打电话的权限? 像小米这些手机权限管理中,是可以禁用的 解决方案 https://www.zhihu.com/question/34654772/answer/60199461 解决方案二: private boolean checkWriteExternalPermission() { String permission = "android.permis

oracle中如何为用户授权

oracle 给用户授权_oracle 用户授权_oracle用户授权 grant(授权)命令 下面对创建的用户user01授权,命令如下: grant connect, resource to user01; (2)revoke(撤消)权限 已授予的权限可以撤消.例如撤消(1)中的授权,命令如下: revoke connect, resource from user01; 一个具有dba角色的用户可以撤消任何别的用户甚至别的dba的connect.resource 和dba的其他权限.当然,这

WordPress中添加留言用户等级显示(头像)功能

方法一  把主题的根目录下的 single.php  (须有评论功能) 复制并重命名为 guestbook.php, 把 comments.php 复制并重命名为 guestcomments.php, 名称可以自定.在 guestbook.php 的开头加入:  代码如下 复制代码 < ?php /* Template Name: GuestBook */ ?> 然后在guestbook.php里找到如下代码: 将  代码如下 复制代码 < ?php comments_template(

SQL Server 2008 数据库中创建只读用户的方法_mssql2008

在SQL Server 2008中,为了保护数据库的安全,需要给不同的使用者开通不同的访问用户,那么如何简单的控制用户的权限呢?下面我们就创建一个只读用户,给大家学习使用. SQL Server 2008 Microsoft SQLServer Management Studio 1.首先打开[Microsoft SQLServer Management Studio],用管理员账户登录.这里我选的服务器是[local],账户是[windows身份验证],如果是连接远程的服务器,输入远程服务器地

MySQL中用户授权以及删除授权的方法_Mysql

用户授权方法 你可以通过发出GRANT语句增加新用户: shell> mysql --user=root mysql mysql> GRANT ALL PRIVILEGES ON *.* TO monty@localhost IDENTIFIED BY 'something' WITH GRANT OPTION; mysql> GRANT ALL PRIVILEGES ON *.* TO monty@"%" IDENTIFIED BY 'something' WITH

MySQL中用户授权/删除授权的方法

用户授权方法 你可以通过发出GRANT语句增加新用户:  代码如下 复制代码 shell> mysql --user=root mysql mysql> GRANT ALL PRIVILEGES ON *.* TO monty@localhost IDENTIFIED BY 'something' WITH GRANT OPTION; mysql> GRANT ALL PRIVILEGES ON *.* TO monty@"%" IDENTIFIED BY 'some

Linux中VPS禁止ROOT授权/登录/禁用root用户的方法

修改ROOT的思路:添加新用户,然后设置该用户为ROOT权限,这样我们以后登录SSH就可以用新用户,这个用户只有我们自己知道,这样就增加了猜测的难度,至此VPS主机的安全进一步得到提升. 第一.新增用户以及设置密码 useradd itbulucom passwd itbulucom 利用useradd添加用户,passwd给这个用户设置密码,然后会出现添加密码界面,我们输入密码之后回车再输入一次. 新增新管理用户 第二.设置权限 vi /etc/ssh/sshd_config 可以用上面的vi

动态Proxy与Java ACL用户访问控制机制实现

用户访问控制(Access control )机制总是围绕粗粒度和细粒度两个方面来讨论: 粗粒度控制:可以规定访问整个对象或对象群的某个层,而细粒度控制则总是在方法或属性层进行控制,比如: 允许一个文件为只读是属于粗粒度控制,而允许对这个文件某行有写操作则属于细粒度控制. 一个好的用户控制机制当然既允许粗粒度也允许细粒度控制,在Jive中我们看到是使用Proxy来达到这个目的,但是我们也发现,由于需要对每个类都要进行细粒度控制,所以必然对每个类都要做一个Proxy类,这样带来了很多Proxy类,

Lumen 5.2 用户授权的例子详解

1.简介 除了提供开箱即用的认证服务之外,Lumen还为资源的授权逻辑和访问控制提供了便捷的方法.你可以通过多种方法和辅助函数组织管理授权逻辑. 通常,Lumen中授权的使用和Laravel中一样,这里我们只讨论不同的地方,更多使用细节请参考完整的Laravel文档. 2.与Laravel的不同之处 定义权限(Abilities) 相对于Laravel,Lumen使用授权主要不同之处在于权限的定义,在Lumen中,你可以在AuthServiceProvider中简单使用Gate门面来定义权限: