从Yii2的源码来分析框架的QueryParamAuth的鉴权过程

Yii是基于PHP语言打造的一款框架,了解PHP的同学对这款框架肯定也不会陌生。而我在最近使用yii2写App接口的时,查看官方了的RESTful Web服务文档,文档中对于授权验证的过程有这样一个介绍:

如果你系那个支持以上3个认证方式,可以使用CompositeAuth,如下所示:

use yii\filters\auth\CompositeAuth;
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;

public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['authenticator'] = [
        'class' => CompositeAuth::className(),
        'authMethods' => [
            HttpBasicAuth::className(),
            HttpBearerAuth::className(),
            QueryParamAuth::className(),
        ],
    ];
    return $behaviors;
}

authMethods 中每个单元应为一个认证方法名或配置数组。

findIdentityByAccessToken()方法的实现是系统定义的, 例如,一个简单的场景,当每个用户只有一个access token, 可存储access token 到user表的access_token列中, 方法可在User类中简单实现,如下所示:

use yii\db\ActiveRecord;
use yii\web\IdentityInterface;

class User extends ActiveRecord implements IdentityInterface
{
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }
}

以上是官方文档的原文,而我当时结合我的API接口,感觉最适合我使用的是第三种QueryParamAuth类型的验证,就是在请求的url中拼接上AccessToken。这也是常见的一种鉴权方式,而实现这些验证,框架又需要我们完成findIdentityByAccessToken()函数,所以为了不稀里糊涂的跟着文档弄完了,我决定从源码里探究一下实现鉴权的过程中究竟发生了什么。

至于如何实现这个过程,以及完成认证,我之前在网上已经看到有大量博客写这个了,所以在此我也不再赘述。

首先我们进入QueryParamAuth类里,里面有一个authenticate($user, $request, $response)函数,这个函数的代码是这样的:

public function authenticate($user, $request, $response)
{
    // 获取get参数里的access-token, $this->tokenParam的具体名称,可以在类中指定
    $accessToken = $request->get($this->tokenParam);
    if (is_string($accessToken)) {
        //存在登录令牌的情况下,执行yii\web\user类里的loginByAccessToken方法
        $identity = $user->loginByAccessToken($accessToken, get_class($this));
        if ($identity !== null) {
            return $identity;
        }
    }
    //没有accessToken直接报错
    if ($accessToken !== null) {
        $this->handleFailure($response);
    }

    return null;
}

我把这段源码的注释直接附在代码中,在这里我们能大致知道这个函数做了哪些事情,但是其中$user, $request, $response这三个参数又是如何获取传递进来的呢。我们再进入QueryParamAuth的父类AuthMethod里一探究竟。

打开这个父类,我们能看到$user, $request, $response这三个参数是父类中定义的三个公开属性。而在父类中的beforeAction()中,获取到这三个方法,

public function beforeAction($action)
{
    $response = $this->response ?: Yii::$app->getResponse();

    try {
        // 这里是重点,父类获取三个参数,传入我们之前的authenticate()函数中。
        $identity = $this->authenticate(
            $this->user ?: Yii::$app->getUser(),
            $this->request ?: Yii::$app->getRequest(),
            $response
        );
    } catch (UnauthorizedHttpException $e) {
        if ($this->isOptional($action)) {
            return true;
        }

        throw $e;
    }

    if ($identity !== null || $this->isOptional($action)) {
        return true;
    }

    $this->challenge($response);
    $this->handleFailure($response);

    return false;
}

我们可以看到,在beforeAction()函数中获取了参数,并且调用了authenticate函数,开始执行认证过程。现在我们知道了参数的由来,那么我们接着回到QueryParamAuth类里的authenticate()函数中,探究鉴权过程。

authenticate()函数中我们看到有这样一行代码:

$identity = $user->loginByAccessToken($accessToken, get_class($this));

在这里我提醒大家,这个$user指的是yii\web\user这个类,而我之前看到很多网上教程让大家去实现loginByAccessToken()这个函数,很多人在实现了这个函数之后问,为什么不调用这个函数,其实这个函数是没有必要实现的,如果你一定要实现这个函数,那么你就得把这里使用的$user替换成你自己的User类,因为在这个时候,还不会调用你在config里配置的user类,很多同学有了问题,还是要先看看源码,而不是发现这篇博客不行,就换另一个博客试试。。。

我们来一起看看loginByAccessToken()函数的代码:

public function loginByAccessToken($token, $type = null)
{
    /* @var $class IdentityInterface */
    // 这里的identityClass才是我们在config中配置的那个User类
    $class = $this->identityClass;

    //这里开始执行我们在User类中实现的findIdentityByAccessToken()函数
    $identity = $class::findIdentityByAccessToken($token, $type);

    //这里的条件,在鉴权之前,我们得是登录状态
    if ($identity && $this->login($identity)) {
        return $identity;
    }

    // 否则 就算有这个用户,但是不是登录状态 一样不能通过验证
    return null;
}

我已经把整个函数的分析写在注释里了,在这个函数里会调用我们在config里配置的User类,并且去执行文档中让我们配置的findIdentityByAccessToken()函数,所以我们写的函数在此时才会派上用场,同时我们还得是登录状态才能通过鉴权,登录的话这里先不展开讲了,可以先用yii框架默认页面的登录就能通过。

至此,我们的登录鉴权就已经通过了,如果不通过的小伙伴,可以再消化一下上面的源码分析,相信你一定能查到问题究竟出现在哪一步的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • date: 2017-11-21 18:15:18title: yii 源码解读 百度脑图 - yii 源码解析:...
    daydaygo阅读 3,416评论 1 11
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • ——人生有很多意想不到的事情:所谓一见钟情,往往是不经意的回眸,却也是上天刻意的安排!这段情,成则白头偕老,不成则...
    幽冥录黄泉笔阅读 240评论 0 0
  • 韶华易逝 因缘际会 于时隙间品锦瑟 弦柱间思年华 聆听枫叶那缓缓飘落的声音 纵使秒速五厘米 也道不尽风的温柔
    隐身的守候阅读 58评论 4 1