Laravel8:Gate源码分析(4)-- 解析授权回调函数

这篇文档中,学习如何找到当初授权时定义的回调函数,主要过程是分析raw方法调用的$this->callAuthCallback() 方法。

在raw()方法中,这句代码调用callAuthCallback()方法。得到一个执行结果

        if (is_null($result)) {
            $result = $this->callAuthCallback($user, $ability, $arguments);
        }

callAuthCallback()方法会运行 授权回调函数,返回执行结果。
但在此之前,需要通过resolveAuthCallback()这个方法,找到对应的授权回调函数。

从下面的代码可以看到,resolveAuthCallback()负责找回调函数,callAuthCallback()方法只要运行回调函数,返回结果就可以了。

    protected function callAuthCallback($user, $ability, array $arguments)
    {
       //得到回调函数
        $callback = $this->resolveAuthCallback($user, $ability, $arguments);
       //运行回调函数,返回执行结果
        return $callback($user, ...$arguments);
    }

接下来最重要的,就是学习 resolveAuthCallback方法。看一下它是如何找到授权按回调函数的。

resolveAuthCallback()

先看一个它的三个参数,user 对应的用户模型对象,ability 是注册授权动作,$arguments 数组类型,是传递进来的参数。

protected function resolveAuthCallback($user, $ability, array $arguments)
    {
        if (isset($arguments[0]) &&
            ! is_null($policy = $this->getPolicyFor($arguments[0])) &&
            $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
            return $callback;
        }

        if (isset($this->stringCallbacks[$ability])) {
            [$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);

            if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
                return $this->abilities[$ability];
            }
        }

        if (isset($this->abilities[$ability]) &&
            $this->canBeCalledWithUser($user, $this->abilities[$ability])) {
            return $this->abilities[$ability];
        }

        return function () {
            //
        };
    }

代码由三个if语句块构成。如果三种if语句都没有命中,最后将回一个空的匿名函数。 如果命中其中某一个if语句,最后将返回匿名函数。

三个if语句分别对应一种专门的注册授权的方法。
(1) 针对使用策略类注册的授权
(2)Gate::define() 注册时,参数2使用数组或字符串
(3)Gate::define() 注册时,参数2使用匿名函数

第一部分

使用策略类方式进行注册授权,并在执行策略检查时,像下面这样使用,就会命中第一部分的代码。

Gate::check("wujin",['lafenfen']);

参数1,wujin是在策略类中定义的方法名。
参数2, 必须使用数组类型,第1个元素必须是策略类的别名。记住是别名。

现在看代码,if里有三个条件,它们之间的关系是&&,必须三个条件都成立。

if (isset($arguments[0]) &&
            ! is_null($policy = $this->getPolicyFor($arguments[0])) &&
            $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
            return $callback;
 }

分析代码

(1) $arguments[0] 
数组首元素,就是注册策略类时使用的别名。必须要存在。
 (2) $this->getPolicyFor($arguments[0])  
根据策略类别名,在类$this->policies属性里,找到真正的注册策略类名。
然后通过容器的make()方法解析策略类,并返回对象。
(3)   $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) 
在策略类对象$policy中,存在$ability方法,并证实它是合法的可调用的结构时,将返回一个自定义的匿名函数

当以上三个条件都成立时。就会返回这个得到的回调函数。

关于这块的详细的分析说明,我把它拆分出来,和Gate策略篇底层代码分析放到了一块,点击链接跳转。

https://www.jianshu.com/p/c0e37ceaf89d

第二部分

当我们注册授权时,参数2使用数组或字符串时。会命中第二部分代码

 Gate::define('addMember', [AdminPolicy::class, 'wujin']);
 或
Gate::define("addMember",AdminPolicy::class);

代码:

if (isset($this->stringCallbacks[$ability])) {
            [$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);

            if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
                return $this->abilities[$ability];
            }
 }

分析:

存储在$this->stringCallbacks 中的内容有两种

第1种通过@符做连接符,连接策略类名和方法名
第2种就是一个单独的类名(类里必须要有__invoke()魔术方法)

(1)$this->stringCallbacks[$ability] =AdminPolicy@'wujin' 
(2)$this->stringCallbacks[$ability] =AdminPolicy 

使用Str::parseCallback($this->stringCallbacks[$ability]) ,以@符作分隔符,将字符串转换为数组。

$class 对应策略类名  $method对应方法名

如果不存在$method时,就会使用 __invoke来替换。
在通过canBeCalledWithUser()测试,当前用户是否可以执行,结果为true时、
就可以返回$this->abilities[$ability]注册的回调函数了。
if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
                return $this->abilities[$ability];
}

第三部分

这部分的代码如下

if (isset($this->abilities[$ability]) && $this->canBeCalledWithUser($user, $this->abilities[$ability])) {
    return $this->abilities[$ability];
}

当注册授权时,define()方法中传入匿名函数时,就会走这块流程

Gate::define("lafenfen",function($user,$id){
      return $id==1;
});

分析代码:

if语句中,需要同时满足两个条件。
第1个 $this->abilities[$ability] 要有注册的授权内容
第2个 $this->canBeCalledWithUser($user, $this->abilities[$ability])  当前用户有执行此方法的权限。

满足上述两个条件后将注册授权的回调函数返回。
return $this->abilities[$ability];

如果上述三种情况都不存在时,直接返回一个空的匿名函数。


return function () {
            //
};

到此resolveAuthCallback()方法分析结束。在学习的过程中,使用的几个其它函数,放在下面进行说明。

canBeCalledWithUser()

作用:确定是否可以用给定的用户调用回调/方法
返回值:boolean

protected function canBeCalledWithUser($user, $class, $method = null)
    {
    //如果$user模型对像不为空时,直接返回true
        if (! is_null($user)) {
            return true;
        }

        if (! is_null($method)) {
            return $this->methodAllowsGuests($class, $method);
        }

        if (is_array($class)) {
            $className = is_string($class[0]) ? $class[0] : get_class($class[0]);

            return $this->methodAllowsGuests($className, $class[1]);
        }

        return $this->callbackAllowsGuests($class);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容