筆者在學習用yii2寫restful api的token認證部分遇到困難,官網教程沒看懂~,解決後,記錄之。
yii的RESTful 授權認證
官方教程鏈接,大概意思如下:
yii2提供了3種驗證token方式,需要在具體控制器指定使用哪種(也可以都使用),這裏以
QueryParams
方式爲例,即通過$_GET參數方式接受token,代碼如下:public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => QueryParamAuth::className(), ]; return $behaviors; }
然後官網說,只需實現
User
類的findIdentityByAccessToken
方法,就實現了認證,代碼如下:class User extends ActiveRecord implements IdentityInterface { public static function findIdentityByAccessToken($token, $type = null) { return static::findOne(['access_token' => $token]); } }
最初看到這裏我是一臉懵逼的,
findIdentityByAccessToken()
的內容我理解,就是用傳入token去用戶表查詢用戶,問題是認證器QueryParamAuth
怎麼找到findIdentityByAccessToken()
的?
看源碼
代碼跳轉到
QueryParamAuth
類,發現其有authenticate()
方法,代碼如下:public function authenticate($user, $request, $response) { $accessToken = $request->get($this->tokenParam); if (is_string($accessToken)) { $identity = $user->loginByAccessToken($accessToken, get_class($this)); if ($identity !== null) { return $identity; } } if ($accessToken !== null) { $this->handleFailure($response); } return null; }
推測是通過
authenticate()
找到User
類的,因爲QueryParamAuth
只有這個方法~,再看了另外兩種認證方式:HttpBasicAuth
和HttpBearerAuth
也有authenticate()
方法(HttpBearerAuth
的在其父類裏),就看它吧。大概意思是,通過
request
組件獲取get參數,然後關鍵是user,使用代碼編輯器跳轉找不到loginByAccessToken
,怎麼辦?推測它也是組件,因爲後兩個參數看來都是組件,User
組件!跳到配置文件,代碼如下:'user' => [ 'identityClass' => 'common\models\User', 'enableAutoLogin' => true, 'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true], ],
Notice:以yii2高級模板爲例的
沒有看到
user
組件指定的class
,也就是說該組件有默認class
,這句話不理解的看這裏。在項目目錄\verdor\yiisoft\yii2\web\Application.php
的找到public function coreComponents() { return array_merge(parent::coreComponents(), [ 'request' => ['class' => 'yii\web\Request'], 'response' => ['class' => 'yii\web\Response'], 'session' => ['class' => 'yii\web\Session'], 'user' => ['class' => 'yii\web\User'], 'errorHandler' => ['class' => 'yii\web\ErrorHandler'], ]); }
跳到
yii\web\User
,發現loginByAccessToken()
方法,不記得它的看前面第3點,代碼如下public function loginByAccessToken($token, $type = null) { /* @var $class IdentityInterface */ $class = $this->identityClass; $identity = $class::findIdentityByAccessToken($token, $type); if ($identity && $this->login($identity)) { return $identity; } return null; }
意思是,調用了
identityClass
屬性的findIdentityByAccessToken()
方法(還記得它嗎!),identityClass
屬性在配置文件指定了,看第4點,findIdentityByAccessToken
就是第二點看得我一臉懵逼的方法。至此,真相大白。還有一點要說明的是,區分第二點的
User
類與User
組件User
組件:很好理解,與其它組件一樣,需要指定類,可通過\Yii::$app->User
找到,理解組件就能很好理解User
組件了。User
類:這個類挺複雜的,有2重身份:- 數據庫模型類:它是用戶表對應的模型,所以,對用戶表的增刪改查都通過它來做,這部分與其它數據庫模型類沒區別。
- 用戶認證類:要有這一身份必須滿足2個條件
- 實現
yii\web\IdentityInterface
接口:implements IdentityInterface
- 在配置文件
User
組件裏指定爲其identityClass
屬性
- 實現
在這一身份角度看,User類是User組件的一部分。
流程總結
控制器指定了認證器 -> 認證器執行authenticate()
-> User
組件的loginByAccessToken()
->User
認證類的findIdentityByAccessToken()
->根據token去user表找user
yii實現token認證具體做法:
前提:數據庫用戶表有token
字段。
- 控制器指定認證器
User
組件指定identityClass
屬性User
類實現IdentityInterface
接口,實現findIdentityByAccessToken()
方法
另外,可能你有如下需求
- 不想在每個控制器裏都寫
behaviors()
聲明認證器 - 某些接口不需要執行認證
下面是我的做法,供參考:
建基類控制器
BaseController
,繼承activeController
,聲明認證器:public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => HttpBearerAuth::className(), // 聲明認證器 ]; return $behaviors; }
在子類控制器,繼承
BaseController
聲明哪些動作不需要認證:public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator']['optional'] = ['action1', 'action2']; // 不需要認證的action return $behaviors; }
總結
筆者通過這個體會到接口的意義,User
類實現IdentityInterface
,就要實現其findIdentityByAccessToken
()方法,否則報錯:接口方法沒實現。爲什麼要設定爲報錯,爲什麼要有 實現了接口,就一定要實現其方法的規定?因爲,很有可能,接口方法在某個地方被調用了!,進而引出對面向對象接口的理解,有興趣的可跳轉:面向對象的接口使用前人代碼的方式