首頁 後端開發 php教程 詳解yii2之restful api授權驗證

詳解yii2之restful api授權驗證

Jan 03, 2018 am 11:42 AM
api restful yii2

本文主要為大家介紹了關於yii2計畫實戰之restful api授權驗證的相關資料,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。希望對大家有幫助。

前言

這篇文章主要針對實際場景中api的部署來寫。

我們今天就來大大的侃侃那些年api遇到的授權驗證問題!

業務分析

我們先來了解整個邏輯

  • 用戶在客戶端填寫登入表單

  • 使用者提交表單,客戶端要求登入介面login

  • 服務端校驗使用者的帳號密碼,並傳回一個有效的token給客戶端

  • 客戶端拿到使用者的token,將之儲存在客戶端例如cookie中

  • 客戶端攜帶token訪問需要校驗的介面例如取得用戶個人資訊介面

  • 服務端校驗token的有效性,校驗通過,反正傳回客戶端所需的資訊,校驗失敗,需要用戶重新登入

本文我們以使用者登錄,取得使用者的個人資訊為例進行詳細的完整版說明。
以上,便是我們本篇文章要實現的重點。先別激動,也別緊張,分析好了之後,細節部分我們再一步一腳印走下去。

準備工作

  • #你應該要有一個api應用程式

  • 對於客戶端,我們準備採用postman進行模擬,如果你的google瀏覽器還沒有安裝postman,請先自行下載

  • 要測試的使用者表需要有一個api_token的字段,沒有的請先自行添加,並保證該字段足夠長度

  • api應用開啟了路由美化,並先配置post類型的login操作和get類型的signup-test操作

  • 關閉了user元件的session會話

關於上面準備工作的第4點和第5點,我們貼一下程式碼方便理解

'components' => [
 'user' => [ 
 'identityClass' => 'common\models\User',
 'enableAutoLogin' => true,
 'enableSession' => false,
 ],
 'urlManager' => [
 'enablePrettyUrl' => true,
 'showScriptName' => false,
 'enableStrictParsing' => true,
 'rules' => [
  [
  'class' => 'yii\rest\UrlRule',
  'controller' => ['v1/user'],
  'extraPatterns' => [
   'POST login' => 'login',
   'GET signup-test' => 'signup-test',
  ]
  ],
 ]
 ],
 // ......
],
登入後複製

signup-test操作我們後面新增測試用戶,為登入操作提供便利。其他類型的操作後面看需要再做添加。

認證類別的選擇

我們在api\modules\v1\controllers\UserController中設定的model類指向common\models\User類,為了說明重點這裡我們就不單獨拿出來重寫了,看各位需要,有必要的話再單獨copy一個User類到api \models下。

校驗使用者權限我們以yii\filters\auth\QueryParamAuth 為例

use yii\filters\auth\QueryParamAuth;

public function behaviors() 
{
 return ArrayHelper::merge (parent::behaviors(), [ 
  'authenticator' => [ 
  'class' => QueryParamAuth::className() 
  ] 
 ] );
}
登入後複製

如此一來,那豈不是所有存取user的操作都需要認證了?那不行,客戶端第一個存取login操作的時候哪來的token,yii\filters\auth\QueryParamAuth對外提供一個屬性,用於過濾不需要驗證的action。我們將UserController的behaviors方法稍作修改

public function behaviors() 
{
 return ArrayHelper::merge (parent::behaviors(), [ 
  'authenticator' => [ 
  'class' => QueryParamAuth::className(),
  'optional' => [
   'login',
   'signup-test'
  ],
  ] 
 ] );
}
登入後複製

這樣login操作就無需權限驗證即可存取了。

新增測試使用者

為了避免讓客戶端登入失敗,我們先寫一個簡單的方法,往user表裡面插入兩條數據,方便接下來的校驗。

UserController增加signupTest操作,注意此方法不屬於講解範圍之內,我們只用於方便測試。

use common\models\User;
/**
 * 添加测试用户
 */
public function actionSignupTest ()
{
 $user = new User();
 $user->generateAuthKey();
 $user->setPassword('123456');
 $user->username = '111';
 $user->email = '111@111.com';
 $user->save(false);

 return [
 'code' => 0
 ];
}
登入後複製

如上,我們新增了一個username是111,密碼是123456的使用者

##登入動作

#假設使用者在客戶端輸入使用者名稱和密碼登入,服務端login操作其實很簡單,大部分的業務邏輯處理都在

api\models\loginForm上,來先看看login的實作

use api\models\LoginForm;

/**
 * 登录
 */
public function actionLogin ()
{
 $model = new LoginForm;
 $model->setAttributes(Yii::$app->request->post());
 if ($user = $model->login()) {
 if ($user instanceof IdentityInterface) {
  return $user->api_token;
 } else {
  return $user->errors;
 }
 } else {
 return $model->errors;
 }
}
登入後複製

登入成功後這裡給客戶端回傳了使用者的token,再來看看登入的具體邏輯的實作


#新建api\models\LoginForm.PHP

<?php
namespace api\models;

use Yii;
use yii\base\Model;
use common\models\User;

/**
 * Login form
 */
class LoginForm extends Model
{
 public $username;
 public $password;

 private $_user;

 const GET_API_TOKEN = &#39;generate_api_token&#39;;

 public function init ()
 {
 parent::init();
 $this->on(self::GET_API_TOKEN, [$this, &#39;onGenerateApiToken&#39;]);
 }


 /**
 * @inheritdoc
 * 对客户端表单数据进行验证的rule
 */
 public function rules()
 {
 return [
  [[&#39;username&#39;, &#39;password&#39;], &#39;required&#39;],
  [&#39;password&#39;, &#39;validatePassword&#39;],
 ];
 }

 /**
 * 自定义的密码认证方法
 */
 public function validatePassword($attribute, $params)
 {
 if (!$this->hasErrors()) {
  $this->_user = $this->getUser();
  if (!$this->_user || !$this->_user->validatePassword($this->password)) {
  $this->addError($attribute, &#39;用户名或密码错误.&#39;);
  }
 }
 }
 /**
 * @inheritdoc
 */
 public function attributeLabels()
 {
 return [
  &#39;username&#39; => &#39;用户名&#39;,
  &#39;password&#39; => &#39;密码&#39;,
 ];
 }
 /**
 * Logs in a user using the provided username and password.
 *
 * @return boolean whether the user is logged in successfully
 */
 public function login()
 {
 if ($this->validate()) {
  $this->trigger(self::GET_API_TOKEN);
  return $this->_user;
 } else {
  return null;
 }
 }

 /**
 * 根据用户名获取用户的认证信息
 *
 * @return User|null
 */
 protected function getUser()
 {
 if ($this->_user === null) {
  $this->_user = User::findByUsername($this->username);
 }

 return $this->_user;
 }

 /**
 * 登录校验成功后,为用户生成新的token
 * 如果token失效,则重新生成token
 */
 public function onGenerateApiToken ()
 {
 if (!User::apiTokenIsValid($this->_user->api_token)) {
  $this->_user->generateApiToken();
  $this->_user->save(false);
 }
 }
}
登入後複製

我們回過頭來看一下,當我們在UserController的login作業中呼叫LoginForm的login作業後都發生了什麼


      1 、呼叫LoginForm的login方法

      2、呼叫validate方法,隨後對rules進行校驗

      3、rules校驗中呼叫validatePassword方法,對使用者名稱及密碼進行校驗

##      4、validatePassword方法校驗的過程中呼叫LoginForm的getUser方法,透過

common\models\User類別的findByUsername取得用戶,找不到或common\models\User類別的findByUsername取得用戶,找不到或common\models\User

#的validatePassword對密碼校驗失敗則回傳error###

5、触发LoginForm::GENERATE_API_TOKEN事件,调用LoginForm的onGenerateApiToken方法,通过common\models\User的apiTokenIsValid校验token的有效性,如果无效,则调用User的generateApiToken方法重新生成

注意:common\models\User类必须是用户的认证类,如果不知道如何创建完善该类,请围观这里 用户管理之user组件的配置

下面补充本节增加的common\models\User的相关方法

/**
 * 生成 api_token
 */
public function generateApiToken()
{
 $this->api_token = Yii::$app->security->generateRandomString() . &#39;_&#39; . time();
}

/**
 * 校验api_token是否有效
 */
public static function apiTokenIsValid($token)
{
 if (empty($token)) {
 return false;
 }

 $timestamp = (int) substr($token, strrpos($token, &#39;_&#39;) + 1);
 $expire = Yii::$app->params[&#39;user.apiTokenExpire&#39;];
 return $timestamp + $expire >= time();
}
登入後複製

继续补充apiTokenIsValid方法中涉及到的token有效期,在api\config\params.php文件内增加即可

<?php
return [
 // ...
 // token 有效期默认1天
 &#39;user.apiTokenExpire&#39; => 1*24*3600,
];
登入後複製

到这里呢,客户端登录 服务端返回token给客户端就完成了。

按照文中一开始的分析,客户端应该把获取到的token存到本地,比如cookie中。以后再需要token校验的接口访问中,从本地读取比如从cookie中读取并访问接口即可。

根据token请求用户的认证操作

假设我们已经把获取到的token保存起来了,我们再以访问用户信息的接口为例。

yii\filters\auth\QueryParamAuth类认定的token参数是 access-token,我们可以在行为中修改下

public function behaviors() 
{
 return ArrayHelper::merge (parent::behaviors(), [ 
   &#39;authenticator&#39; => [ 
    &#39;class&#39; => QueryParamAuth::className(),
    &#39;tokenParam&#39; => &#39;token&#39;,
    &#39;optional&#39; => [
     &#39;login&#39;,
     &#39;signup-test&#39;
    ],
   ] 
 ] );
}
登入後複製

这里将默认的access-token修改为token。

我们在配置文件的urlManager组件中增加对userProfile操作

&#39;extraPatterns&#39; => [
 &#39;POST login&#39; => &#39;login&#39;,
 &#39;GET signup-test&#39; => &#39;signup-test&#39;,
 &#39;GET user-profile&#39; => &#39;user-profile&#39;,
]
登入後複製

我们用postman模拟请求访问下 /v1/users/user-profile?token=apeuT9dAgH072qbfrtihfzL6qDe_l4qz_1479626145发现,抛出了一个异常

\"findIdentityByAccessToken\" is not implemented.<br/>

这是怎么回事呢?

我们找到 yii\filters\auth\QueryParamAuth 的authenticate方法,发现这里调用了 common\models\User类的loginByAccessToken方法,有同学疑惑了,common\models\User类没实现loginByAccessToken方法为啥说findIdentityByAccessToken方法没实现?如果你还记得common\models\User类实现了yii\web\user类的接口的话,你应该会打开yii\web\User类找答案。没错,loginByAccessToken方法在yii\web\User中实现了,该类中调用了common\models\User的findIdentityByAccessToken,但是我们看到,该方法中通过throw抛出了异常,也就是说这个方法要我们自己手动实现!

这好办了,我们就来实现下common\models\User类的findIdentityByAccessToken方法吧

public static function findIdentityByAccessToken($token, $type = null)
{
 // 如果token无效的话,
 if(!static::apiTokenIsValid($token)) {
  throw new \yii\web\UnauthorizedHttpException("token is invalid.");
 }

 return static::findOne([&#39;api_token&#39; => $token, &#39;status&#39; => self::STATUS_ACTIVE]);
 // throw new NotSupportedException(&#39;"findIdentityByAccessToken" is not implemented.&#39;);
}
登入後複製

验证完token的有效性,下面就要开始实现主要的业务逻辑部分了。

/**
 * 获取用户信息
 */
public function actionUserProfile ($token)
{
 // 到这一步,token都认为是有效的了
 // 下面只需要实现业务逻辑即可,下面仅仅作为案例,比如你可能需要关联其他表获取用户信息等等
 $user = User::findIdentityByAccessToken($token);
 return [
  &#39;id&#39; => $user->id,
  &#39;username&#39; => $user->username,
  &#39;email&#39; => $user->email,
 ];
}
登入後複製

服务端返回的数据类型定义

在postman中我们可以以何种数据类型输出的接口的数据,但是,有些人发现,当我们把postman模拟请求的地址copy到浏览器地址栏,返回的又却是xml格式了,而且我们明明在UserProfile操作中返回的是属组,怎么回事呢?

这其实是官方捣的鬼啦,我们一层层源码追下去,发现在yii\rest\Controller类中,有一个 contentNegotiator行为,该行为指定了允许返回的数据格式formats是json和xml,返回的最终的数据格式根据请求头中Accept包含的首先出现在formats中的为准,你可以在yii\filters\ContentNegotiatornegotiateContentType方法中找到答案。

你可以在浏览器的请求头中看到

Accept:

text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

即application/xml首先出现在formats中,所以返回的数据格式是xml类型,如果客户端获取到的数据格式想按照json进行解析,只需要设置请求头的Accept的值等于application/json即可

有同学可能要说,这样太麻烦了,啥年代了,谁还用xml,我就想服务端输出json格式的数据,怎么做?

办法就是用来解决问题滴,来看看怎么做。api\config\main.php文件中增加对response的配置

&#39;response&#39; => [
 &#39;class&#39; => &#39;yii\web\Response&#39;,
 &#39;on beforeSend&#39; => function ($event) {
  $response = $event->sender;
  $response->format = yii\web\Response::FORMAT_JSON;
 },
],
登入後複製

如此,不管你客户端传什么,服务端最终输出的都会是json格式的数据了。

自定义错误处理机制

再来看另外一个比较常见的问题:

你看我们上面几个方法哈,返回的结果是各式各样的,这样就给客户端解析增加了困扰,而且一旦有异常抛出,返回的代码还都是一堆一堆的,头疼,怎么办?

说到这个问题之前呢,我们先说一下yii中先关的异常处理类,当然,有很多哈。比如下面常见的一些,其他的自己去挖掘

yii\web\BadRequestHttpException
yii\web\ForbiddenHttpException
yii\web\NotFoundHttpException
yii\web\ServerErrorHttpException
yii\web\UnauthorizedHttpException
yii\web\TooManyRequestsHttpException
登入後複製

实际开发中各位要善于去利用这些类去捕获异常,抛出异常。说远了哈,我们回到重点,来说如何自定义接口异常响应或者叫自定义统一的数据格式,比如向下面这种配置,统一响应客户端的格式标准。

&#39;response&#39; => [
 &#39;class&#39; => &#39;yii\web\Response&#39;,
 &#39;on beforeSend&#39; => function ($event) {
  $response = $event->sender;
  $response->data = [
   &#39;code&#39; => $response->getStatusCode(),
   &#39;data&#39; => $response->data,
   &#39;message&#39; => $response->statusText
  ];
  $response->format = yii\web\Response::FORMAT_JSON;
 },
],
登入後複製

说道了那么多,本文就要结束了,刚开始接触的同学可能有一些蒙,不要蒙,慢慢消化,先知道这么个意思,了解下restful api接口在整个过程中是怎么用token授权的就好。这样真正实际用到的时候,你也能举一反三!

相关推荐:

Yii2整合迅搜实现高效中文分词检索

Yii如何过滤不良代码

Yii2实现rbac权限控制

以上是詳解yii2之restful api授權驗證的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何在PHP專案中透過呼叫API介面來實現資料的爬取與處理? 如何在PHP專案中透過呼叫API介面來實現資料的爬取與處理? Sep 05, 2023 am 08:41 AM

如何在PHP專案中透過呼叫API介面來實現資料的爬取與處理?一、介紹在PHP專案中,我們經常需要爬取其他網站的數據,並對這些數據進行處理。而許多網站提供了API接口,我們可以透過呼叫這些接口來取得資料。本文將介紹如何使用PHP來呼叫API接口,實現資料的爬取與處理。二、取得API介面的URL和參數在開始之前,我們需要先取得目標API介面的URL以及所需的

Oracle API使用指南:探索資料介面技術 Oracle API使用指南:探索資料介面技術 Mar 07, 2024 am 11:12 AM

Oracle是一家全球知名的資料庫管理系統供應商,其API(ApplicationProgrammingInterface,應用程式介面)是一種強大的工具,可協助開發人員輕鬆地與Oracle資料庫互動和整合。在本文中,我們將深入探討OracleAPI的使用指南,向讀者展示如何在開發過程中利用資料介面技術,同時提供具體的程式碼範例。 1.Oracle

如何處理Laravel API報錯問題 如何處理Laravel API報錯問題 Mar 06, 2024 pm 05:18 PM

標題:如何處理LaravelAPI報錯問題,需要具體程式碼範例在進行Laravel開發時,常會遇到API報錯的情況。這些報錯可​​能來自於程式碼邏輯錯誤、資料庫查詢問題或是外部API請求失敗等多種原因。如何處理這些報錯是一個關鍵的問題,本文將透過具體的程式碼範例來示範如何有效處理LaravelAPI報錯問題。 1.錯誤處理在Laravel

Oracle API整合策略解析:實現系統間無縫通信 Oracle API整合策略解析:實現系統間無縫通信 Mar 07, 2024 pm 10:09 PM

OracleAPI整合策略解析:實現系統間無縫通信,需要具體程式碼範例在當今數位化時代,企業內部系統之間需要相互通信和資料共享,而OracleAPI就是幫助實現系統間無縫通信的重要工具之一。本文將從OracleAPI的基本概念和原則入手,探討API整合的策略,最終給出具體的程式碼範例幫助讀者更好地理解和應用OracleAPI。一、OracleAPI基本

React API呼叫指南:如何與後端API互動和資料傳輸 React API呼叫指南:如何與後端API互動和資料傳輸 Sep 26, 2023 am 10:19 AM

ReactAPI呼叫指南:如何與後端API進行互動和資料傳輸概述:在現代的Web開發中,與後端API進行互動和資料傳輸是一個常見的需求。 React作為一個流行的前端框架,提供了一些強大的工具和功能來簡化這個過程。本文將介紹如何使用React來呼叫後端API,包括基本的GET和POST請求,並提供具體的程式碼範例。安裝所需的依賴:首先,確保在專案中安裝了Axi

開發建議:如何利用ThinkPHP框架進行API開發 開發建議:如何利用ThinkPHP框架進行API開發 Nov 22, 2023 pm 05:18 PM

開發建議:如何利用ThinkPHP框架進行API開發隨著網際網路的不斷發展,API(ApplicationProgrammingInterface)的重要性也日益凸顯。 API是不同應用程式之間進行通訊的橋樑,它可以實現資料共享、功能呼叫等操作,為開發者提供了相對簡單且快速的開發方式。而ThinkPHP框架作為一款優秀的PHP開發框架,具有高效能、可擴展且易用

使用Python將API資料儲存為CSV格式 使用Python將API資料儲存為CSV格式 Aug 31, 2023 pm 09:09 PM

在資料驅動的應用程式和分析領域,API(應用程式介面)在從各種來源檢索資料方面發揮著至關重要的作用。使用API​​資料時,通常需要以易於存取和操作的格式儲存資料。其中一種格式是CSV(逗號分隔值),它允許有效地組織和儲存表格資料。本文將探討使用強大的程式語言Python將API資料儲存為CSV格式的過程。透過遵循本指南中概述的步驟,我們將學習如何從API中檢索資料、提取相關資訊並將其儲存在CSV檔案中以供進一步分析和處理。讓我們深入了解使用Python進行API資料處理的世界,並釋放CSV格式的潛

如何使用MongoDB開發一個簡單的 CRUD API 如何使用MongoDB開發一個簡單的 CRUD API Sep 19, 2023 pm 12:32 PM

如何使用MongoDB開發一個簡單的CRUDAPI在現代的網路應用程式開發中,CRUD(增刪改查)操作是非常常見且重要的功能之一。在本文中,我們將介紹如何使用MongoDB資料庫開發一個簡單的CRUDAPI,並提供具體的程式碼範例。 MongoDB是一個開源的NoSQL資料庫,它以文件的形式儲存資料。與傳統的關聯式資料庫不同,MongoDB沒有預先定義的模式

See all articles