Yii2.0 RESTful API 認證教程

認證介紹

和Web應用不一樣,RESTful APIs 一般是無狀態的, 也就意味着不該使用 sessionscookies, 所以每一個請求應附帶某種受權憑證,由於用戶受權狀態可能沒經過 sessionscookies 維護, 經常使用的作法是每一個請求都發送一個祕密的 access token 來認證用戶, 因爲 access token 能夠惟一識別和認證用戶,API 請求應經過 HTTPS 來防止man-in-the-middle (MitM) 中間人攻擊.php

認證方式

  • 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 權威指南web

實現步驟

咱們都知道 Yii2.0 默認的認證類都是 User,先後臺都是共用一個認證類,所以咱們要把API 認證類 單獨分離出來,達到前、後、API都分離, 繼上一章:(這裏暫時使用默認User數據表,正式環境請分離不一樣的數據表來進行認證)數據庫

準備條件api

繼上篇的 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');
    }
複製代碼

執行遷移命令bash

php yii migrate
複製代碼

瀏覽器打開前臺目錄 frontend 頁面,點擊註冊帳號,先註冊一個帳號服務器

配置

打開 api\config\main.phpcookie

配置 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() 方法.

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

添加一個REST控制器

  • 因我這裏暫未設計其餘數據表 因此咱們暫且還使用User 數據表吧
  1. 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

  1. 實現 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]);
    }
    ...
}
複製代碼
  1. 爲剛纔新加的控制器添加路由規則(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了?

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

  1. http://youdomain/articles?access-token=y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920
  2. 傳遞 header頭信息
Authorization:Bearer y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920
複製代碼

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

好啦,基於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' => '用戶名或密碼錯誤!'
    ];
複製代碼

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

相關文章
相關標籤/搜索