国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

香港云服务器
服務器之家 - 編程語言 - PHP教程 - yii2項目實戰之restful api授權驗證詳解

yii2項目實戰之restful api授權驗證詳解

2021-05-20 17:14lhorse003 PHP教程

這篇文章主要給大家介紹了關于yii2項目實戰之restful api授權驗證的相關資料,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。

前言

什么是restful風格的api呢?我們之前有寫過大篇的文章來介紹其概念以及基本操作。

既然寫過了,那今天是要說點什么嗎?

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

我們今天就來大大的侃侃那些年api遇到的授權驗證問題!獨家干活,如果看完有所受益,記得不要忘記給我點贊哦。

業務分析

我們先來了解一下整個邏輯

  • 用戶在客戶端填寫登錄表單
  • 用戶提交表單,客戶端請求登錄接口login
  • 服務端校驗用戶的帳號密碼,并返回一個有效的token給客戶端
  • 客戶端拿到用戶的token,將之存儲在客戶端比如cookie中
  • 客戶端攜帶token訪問需要校驗的接口比如獲取用戶個人信息接口
  • 服務端校驗token的有效性,校驗通過,反正返回客戶端需要的信息,校驗失敗,需要用戶重新登錄

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

準備工作

  • 你應該有一個api應用,如果你還沒有,請先移步這里→_→Restful api基礎
  • 對于客戶端,我們準備采用postman進行模擬,如果你的google瀏覽器還沒有安裝postman,請先自行下載
  • 要測試的用戶表需要有一個api_token的字段,沒有的請先自行添加,并保證該字段足夠長度
  • api應用開啟了路由美化,并先配置post類型的login操作和get類型的signup-test操作
  • 關閉了user組件的session會話

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'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 為例

?
1
2
3
4
5
6
7
8
9
10
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方法稍作修改

?
1
2
3
4
5
6
7
8
9
10
11
12
public function behaviors()
{
 return ArrayHelper::merge (parent::behaviors(), [
  'authenticator' => [
  'class' => QueryParamAuth::className(),
  'optional' => [
   'login',
   'signup-test'
  ],
  ]
 ] );
}

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

添加測試用戶

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * 登錄
 */
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<?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 = 'generate_api_token';
 
 public function init ()
 {
 parent::init();
 $this->on(self::GET_API_TOKEN, [$this, 'onGenerateApiToken']);
 }
 
 
 /**
 * @inheritdoc
 * 對客戶端表單數據進行驗證的rule
 */
 public function rules()
 {
 return [
  [['username', 'password'], 'required'],
  ['password', 'validatePassword'],
 ];
 }
 
 /**
 * 自定義的密碼認證方法
 */
 public function validatePassword($attribute, $params)
 {
 if (!$this->hasErrors()) {
  $this->_user = $this->getUser();
  if (!$this->_user || !$this->_user->validatePassword($this->password)) {
  $this->addError($attribute, '用戶名或密碼錯誤.');
  }
 }
 }
 /**
 * @inheritdoc
 */
 public function attributeLabels()
 {
 return [
  'username' => '用戶名',
  'password' => '密碼',
 ];
 }
 /**
 * 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的validatePassword對密碼校驗失敗則返回error

      5、觸發LoginForm::GENERATE_API_TOKEN事件,調用LoginForm的onGenerateApiToken方法,通過common\models\User的apiTokenIsValid校驗token的有效性,如果無效,則調用User的generateApiToken方法重新生成

注意:common\models\User類必須是用戶的認證類,如果不知道如何創建完善該類,請圍觀這里 用戶管理之user組件的配置

下面補充本節增加的common\models\User的相關方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 生成 api_token
 */
public function generateApiToken()
{
 $this->api_token = Yii::$app->security->generateRandomString() . '_' . time();
}
 
/**
 * 校驗api_token是否有效
 */
public static function apiTokenIsValid($token)
{
 if (empty($token)) {
 return false;
 }
 
 $timestamp = (int) substr($token, strrpos($token, '_') + 1);
 $expire = Yii::$app->params['user.apiTokenExpire'];
 return $timestamp + $expire >= time();
}

繼續補充apiTokenIsValid方法中涉及到的token有效期,在api\config\params.php文件內增加即可

?
1
2
3
4
5
6
<?php
return [
 // ...
 // token 有效期默認1天
 'user.apiTokenExpire' => 1*24*3600,
];

到這里呢,客戶端登錄 服務端返回token給客戶端就完成了。

按照文中一開始的分析,客戶端應該把獲取到的token存到本地,比如cookie中。以后再需要token校驗的接口訪問中,從本地讀取比如從cookie中讀取并訪問接口即可。

根據token請求用戶的認證操作

假設我們已經把獲取到的token保存起來了,我們再以訪問用戶信息的接口為例。

yii\filters\auth\QueryParamAuth類認定的token參數是 access-token,我們可以在行為中修改下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public function behaviors()
{
 return ArrayHelper::merge (parent::behaviors(), [
   'authenticator' => [
    'class' => QueryParamAuth::className(),
    'tokenParam' => 'token',
    'optional' => [
     'login',
     'signup-test'
    ],
   ]
 ] );
}

這里將默認的access-token修改為token。

我們在配置文件的urlManager組件中增加對userProfile操作

?
1
2
3
4
5
'extraPatterns' => [
 'POST login' => 'login',
 'GET signup-test' => 'signup-test',
 'GET user-profile' => 'user-profile',
]

我們用postman模擬請求訪問下 /v1/users/user-profile?token=apeuT9dAgH072qbfrtihfzL6qDe_l4qz_1479626145發現,拋出了一個異常

\"findIdentityByAccessToken\" is not implemented.

這是怎么回事呢?

我們找到 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方法吧

?
1
2
3
4
5
6
7
8
9
10
public static function findIdentityByAccessToken($token, $type = null)
{
 // 如果token無效的話,
 if(!static::apiTokenIsValid($token)) {
  throw new \yii\web\UnauthorizedHttpException("token is invalid.");
 }
 
 return static::findOne(['api_token' => $token, 'status' => self::STATUS_ACTIVE]);
 // throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}

驗證完token的有效性,下面就要開始實現主要的業務邏輯部分了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * 獲取用戶信息
 */
public function actionUserProfile ($token)
{
 // 到這一步,token都認為是有效的了
 // 下面只需要實現業務邏輯即可,下面僅僅作為案例,比如你可能需要關聯其他表獲取用戶信息等等
 $user = User::findIdentityByAccessToken($token);
 return [
  'id' => $user->id,
  'username' => $user->username,
  'email' => $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的配置

?
1
2
3
4
5
6
7
'response' => [
 'class' => 'yii\web\Response',
 'on beforeSend' => function ($event) {
  $response = $event->sender;
  $response->format = yii\web\Response::FORMAT_JSON;
 },
],

如此,不管你客戶端傳什么,服務端最終輸出的都會是json格式的數據了。

自定義錯誤處理機制

再來看另外一個比較常見的問題:

你看我們上面幾個方法哈,返回的結果是各式各樣的,這樣就給客戶端解析增加了困擾,而且一旦有異常拋出,返回的代碼還都是一堆一堆的,頭疼,怎么辦?

說到這個問題之前呢,我們先說一下yii中先關的異常處理類,當然,有很多哈。比如下面常見的一些,其他的自己去挖掘

?
1
2
3
4
5
6
yii\web\BadRequestHttpException
yii\web\ForbiddenHttpException
yii\web\NotFoundHttpException
yii\web\ServerErrorHttpException
yii\web\UnauthorizedHttpException
yii\web\TooManyRequestsHttpException

實際開發中各位要善于去利用這些類去捕獲異常,拋出異常。說遠了哈,我們回到重點,來說如何自定義接口異常響應或者叫自定義統一的數據格式,比如向下面這種配置,統一響應客戶端的格式標準。

?
1
2
3
4
5
6
7
8
9
10
11
12
'response' => [
 'class' => 'yii\web\Response',
 'on beforeSend' => function ($event) {
  $response = $event->sender;
  $response->data = [
   'code' => $response->getStatusCode(),
   'data' => $response->data,
   'message' => $response->statusText
  ];
  $response->format = yii\web\Response::FORMAT_JSON;
 },
],

說道了那么多,本文就要結束了,剛開始接觸的同學可能有一些蒙,不要蒙,慢慢消化,先知道這么個意思,了解下restful api接口在整個過程中是怎么用token授權的就好。這樣真正實際用到的時候,你也能舉一反三!

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:http://blog.csdn.net/lhorse003/article/details/62215672

延伸 · 閱讀

精彩推薦
602
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40 Weibo Article 41 Weibo Article 42
主站蜘蛛池模板: 国产精品免费视频一区 | 成人黄色免费在线视频 | www.日韩| 亚洲不卡| 日韩高清一区 | 国产精品1 | 成人在线视频网 | 国产尤物一区 | 色噜噜狠狠一区二区三区狼国成人 | 久久精品国产清自在天天线 | 2018啪一啪 | 欧美日韩精品综合 | 久久精品电影网 | 亚洲欧美国产日韩综合 | 色婷婷精品国产一区二区三区 | 国产精品毛片在线 | 91麻豆精品国产91久久久更新资源速度超快 | 一区二区中文字幕 | 不卡视频一区二区 | 久久精品久久久 | 一道本一二三区 | 欧美专区在线观看 | 49vv看片免费 | 99精品99 | 亚洲欧美在线观看 | 九九综合| 精品乱子伦一区二区三区 | 亚洲日本va中文字幕 | 欧美日韩精品一区二区三区四区 | 久久性网站 | 亚洲视频在线一区 | 精品成人久久 | 日韩视频在线一区二区 | 国产美女在线播放 | 亚洲视频在线播放 | 99精品一区二区 | 久久久精品免费观看 | 毛片在线观看网站 | 操操操操操操 | 久久9国产偷伦 | 爱免费视频|