Yii2.0 RESTful API 認證教程

hedeqiang發表於2019-06-06

隔了怎麼長時間,終於到了 Yii2.0 RESTful API 認證介紹了.

廢話不多說,直接正文開始

和Web應用不同,RESTful APIs 通常是無狀態的, 也就意味著不應使用 sessionscookies, 因此每個請求應附帶某種授權憑證,因為使用者授權狀態可能沒通過 sessionscookies 維護, 常用的做法是每個請求都傳送一個祕密的 access token 來認證使用者, 由於 access token 可以唯一識別和認證使用者,API 請求應通過 HTTPS 來防止man-in-the-middle (MitM) 中間人攻擊.

  • HTTP 基本認證 :access token 當作使用者名稱傳送,應用在access token可安全存在API使用端的場景, 例如,API使用端是執行在一臺伺服器上的程式。
  • 請求引數: access token 當作API URL請求引數傳送,例如 https://example.com/users?access-token=xxxxxxxx, 由於大多數伺服器都會儲存請求引數到日誌, 這種方式應主要用於JSONP 請求,因為它不能使用HTTP頭來傳送 access token
  • OAuth 2 : 使用者從認證伺服器上獲取基於 OAuth2 協議的 access token, 然後通過 HTTP Bearer Tokens 傳送到 API 伺服器。

上方進行簡單介紹,內容來自 Yii Framework 2.0 權威指南

我們都知道 Yii2.0 預設的認證類都是 User,前後臺都是共用一個認證類,因此我們要把API 認證類 單獨分離出來,達到前、後、API都分離,
繼上一章:(這裡暫時使用預設User資料表,正式環境請分離不同的資料表來進行認證)

準備條件

繼上篇的 User 資料表,我們還需要增加一 個access_token 的欄位,
1.直接在你的資料庫中新增 access_token 欄位。
2.使用資料遷移的方式
進入專案根目錄開啟控制檯輸入以下命令:

php yii migrate/create add_access_token_to_user

開啟 你的專案目錄/console/migrations/m180704_054630_add_access_token_to_user.php 修改如下內容:

public function safeUp()
{
    $this->addColumn('user', 'access_token', $this->string());
}
public function safeDown()
{
    $this->dropColumn('user', 'access_token');
}

執行遷移命令

php yii migrate

配置

開啟 api\config\main.php

配置 user 應用元件:

  • 設定 identityClass 屬性為哪個認證類
  • 設定 enableSession 屬性為 false
  • 設定 enableAutoLogin 屬性為 true

session 元件註釋掉,或刪掉

'user' => [
    'identityClass' => 'api\models\User',
    'enableAutoLogin' => true,
    'enableSession'=>false,
    //'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
],
//'session' => [ // this is the name of the session cookie used for login on the backend
//  'name' => 'advanced-backend',
//],

編寫 api\models\User.php 實現認證類,繼承 IdentityInterface

common\models\User 類拷貝到 api\models\目錄下,修改名稱空間為api\models

<?php
namespace api\models;

use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
...
class User extends ActiveRecord implements IdentityInterface
{
    ...
    ...
}

common\models\LoginForm.php 類拷貝到api\models\目錄下,修改名稱空間,並重寫login方法:

<?php
namespace api\models;

use Yii;
use yii\base\Model;
...
...

public function login()
{
    if ($this->validate()) {
        $access_token=$this->_user->generateAccessToken();
        $this->_user->save();
        return $access_token;
    } else {
        return false;
    }
}

上方程式碼給User模型新增了一個generateAccessToken()方法,因此我們到api\models\User.php中新增此方法

namespace api\models;

use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
...
...
class User extends ActiveRecord implements IdentityInterface
{
    ...
    ...

    /**
     * 生成accessToken字串
     * @return string
     * @throws \yii\base\Exception
     */
    public function generateAccessToken()
    {
        $this->access_token=Yii::$app->security->generateRandomString();
        return $this->access_token;
    }
}

接下來開啟 之前的User 控制器編寫登入方法

use api\models\LoginForm;
...
... //省略一些程式碼

/**
 * 登陸
 * @return array
 * @throws \yii\base\Exception
 * @throws \yii\base\InvalidConfigException
 */
public function actionLogin()
{
    $model = new LoginForm();
    if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) {
        return [
            'access_token' => $model->login(),
        ];
    } else {
        return $model->getFirstErrors();
    }
}
...

最後新增一條URL規則

開啟 api\config\main.php 修改 components屬性,新增下列程式碼:

'urlManager' => [
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
        ['class' => 'yii\rest\UrlRule', 
            'controller' => 'user',
            'extraPatterns'=>[
                'POST login'=>'login',
            ],
        ],
    ],
]

使用一個除錯工具來進行測試 http://youdomain/users/login 記住是POST 請求傳送,假如用POSTMAN有問題的話指定一下 Content-Type:application/x-www-form-urlencoded

ok,不出意外的話,相信你已經可以收到一個access_token了,接下來就是如何使用這個token,如何維持認證狀態,達到不攜帶這個token將無法訪問,返回401

實現認證只需兩步:

  1. 在你的 REST 控制器類中配置 authenticator 行為來指定使用哪種認證方式
  2. 在你的 user identity class 類中實現 [yii\web\IdentityInterface::findIdentityByAccessToken()](https://www.yiichina.com/doc/api/2.0/yii-w...) 方法.

接下來我們圍繞這兩步來實現:

新增一個REST控制器

因我這裡暫未設計其他資料表 所以我們暫且還使用User 資料表吧

api\controllers\新加一個控制器 命名為 ArticleController 並繼承 yii\rest\ActiveController,配置認證方式程式碼:程式碼如下:

<?php
namespace api\controllers;

use yii\rest\ActiveController;
use Yii;
use yii\filters\auth\CompositeAuth;
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;

class ArticleController extends ActiveController
{
    public $modelClass = 'api\models\User';
    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => CompositeAuth::className(),
            'authMethods' => [
                HttpBasicAuth::className(),
                HttpBearerAuth::className(),
                QueryParamAuth::className(),
            ],
        ];
        return $behaviors;
    }
}

注意:這個控制器並非真正的Article,實則還是User

實現 findIdentityByAccessToken() 方法:

開啟 api\models\User.php 重寫 findIdentityByAccessToken() 方法

...
...
class User extends ActiveRecord implements IdentityInterface
{
    ...
    ...

    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }
    ...
}

為剛才新加的控制器新增路由規則(ps:好像多了一步......)

修改 api\config\main.php

'urlManager' => [
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
        ['class' => 'yii\rest\UrlRule',
            'controller' => 'user',
            'extraPatterns'=>[
                'GET send-email'=>'send-email'
                'POST login'=>'login',
            ],
        ],
        ['class' => 'yii\rest\UrlRule',
            'controller' => 'article',
            'extraPatterns'=>[

            ],
        ],
    ],
]

接下來訪問一下你的域名 http://youdomain/articles,不攜帶任何引數是不是返回 401了?

file

上面的格式本章並不存在,你只要返回 401 即可

ok,這裡介紹兩種訪問方式,一種是URL訪問,另一種是通過header 來進行攜帶

  1. http://youdomain/articles?access-token=y3X...
  2. 傳遞 header頭資訊
    Authorization:Bearer y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920

    注意 Bearer 和你的token中間是有 一個空格的,很多同學在這個上面碰了很多次

file

好啦,基於YII2.0 RESTful 認證就此結束了,

更過完整的功能 請移步官方文件
授權驗證
另外還有速率驗證,就自行發覺吧
另外,如果看不懂,或者寫的不好,請移步 魏曦 老師的視訊教程,本人所有內容都是跟隨 魏曦老師 學的
魏曦教你學

寫完認證發現我們的介面返回的資料不是很直觀,現實生活中通常也不是這樣子的,我們可能會返回一些特定的格式

自定義響應內容

開啟 api\config\main.phpcomponents陣列裡面新增如下內容分


'response' => [
    'class' => 'yii\web\Response',
    'on beforeSend' => function ($event) {
        $response = $event->sender;
        $response->data = [
            'success' => $response->isSuccessful,
            'code' => $response->getStatusCode(),
            'message' => $response->statusText,
            'data' => $response->data,
        ];
        $response->statusCode = 200;
    },
],

這裡的狀態碼統一設為 200 ,具體的可另行配置,假如登陸操作 密碼錯誤或者其他,我們可以在控制器中這樣使用:

$response = Yii::$app->response;
$response->setStatusCode(422);
return [
    'errmsg' => '使用者名稱或密碼錯誤!'
];

水平有限,難免有紕漏,請不吝賜教,在下會感激不盡

相關文章