1. 程式人生 > >Yii2.0 使用者登入詳解(上)

Yii2.0 使用者登入詳解(上)

一、準備

在開始編寫程式碼之前,我們需要思考一下:使用者登陸模組,實現的是什麼功能?很明顯,是登陸功能,那麼,登陸需要使用者名稱和密碼,我們在資料庫的一張表中就應該準備好使用者名稱和密碼的欄位,再思考一下,如果要實現自動登陸的功能,那麼還需要什麼?Cookie,是專門用於自動登陸的,所以,我們的資料表可能需要準備一個欄位,專門用於儲存客戶端登陸所生成的cookie,這樣,就能通過驗證客戶端和服務端的cookie是否相同來進行自動登陸了。基於以上思考,我們的資料表應該包含以下欄位: id(primarykey,auto_increment),username(varchar),password(varchar(32)),auth_key(varchar(32)),accessToken(varchar(32))(這個暫不解釋,後文解釋).      1、首先,建立一個數據庫:myDatabase,      2、然後建立一張資料表:user,增加上述欄位。      對於如何建資料庫和建表,這裡不再贅述。

二、模型(Model)

Yii框架採用MVC設計模式,所以Model是一個模組的核心所在,所以我們先完成對Model的編寫。

      1、LoginForm.php

使用者登陸模組,所提交的是username和password,所以我們要先建立一個Model,專門處理使用者提交的資料,所以先新建一個LoginForm.php,以下為程式碼:

  1. <?php  
  2. namespace app\modules\backend\models;  
  3. use Yii;  
  4. use yii\base\Model;  
  5. /** 
  6.  * LoginForm is the model behind the login form. 
  7.  */  
  8. class LoginForm extends Model  
  9. {  
  10.     public $username;  
  11.     public $password;  
  12.     public $rememberMe = true;  
  13.     private $_user = false;  
  14.     /** 
  15.      * @return array the validation rules. 
  16.      */  
  17.     public function rules()<span style="white-space:pre">     </span>//①  
  18.     {  
  19.         return [  
  20.             // username and password are both required  
  21.             [['username', 'password'], 'required','message'=>""],  
  22.             // rememberMe must be a boolean value  
  23.             ['rememberMe', 'boolean'],  
  24.             // password is validated by validatePassword()  
  25.             ['password', 'validatePassword'],  
  26.         ];  
  27.     }  
  28.     /** 
  29.      * Validates the password. 
  30.      * This method serves as the inline validation for password. 
  31.      * 
  32.      * @param string $attribute the attribute currently being validated 
  33.      * @param array $params the additional name-value pairs given in the rule 
  34.      */  
  35.     public function validatePassword($attribute, $params)  
  36.     {  
  37.         if (!$this->hasErrors()) {  
  38.             $user = $this->getUser();  
  39.             if (!$user || !$user->validatePassword($this->password)) {  
  40.                 $this->addError($attribute, 'Incorrect username or password.');  
  41.             }  
  42.         }  
  43.     }  
  44.     /** 
  45.      * Logs in a user using the provided username and password. 
  46.      * @return boolean whether the user is logged in successfully 
  47.      */  
  48.     public function login()  
  49.     {  
  50.         if ($this->validate()) {  
  51.             if($this->rememberMe)  
  52.             {  
  53.                 $this->_user->generateAuthKey();//③  
  54.             }  
  55.             return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);  
  56.         }  
  57.         return false;  
  58.     }  
  59.     /** 
  60.      * Finds user by [[username]] 
  61.      * 
  62.      * @return User|null 
  63.      */  
  64.     public function getUser()  
  65.     {  
  66.         if ($this->_user === false) {  
  67.             $this->_user = User::findByUsername($this->username); //②  
  68.         }  
  69.         return $this->_user;  
  70.     }  
  71. }  

該Model是根據basic模板自帶的LoginForm修改而成,程式碼中大多有註釋,這裡關注以下程式碼:

①號程式碼處是rules規則,rules規則定義了填充過來的資料的規則,驗證所填的資料是否為空,是否符合格式之類的,其中有一欄是password,對應的規則是validatePassword,會自動呼叫當前類的validatePassword()方法,注意與下文的User類對應的方法區分。

②號程式碼,呼叫了User類裡面的findByUsername方法,這個User類下面會寫到,主要是為了返回一個AR類例項,與當前LoginForm的資料進行比較。

③號程式碼,這裡暫時不提,等講到cookie登陸的時候再提。


2、User.php

(1)ActiveRecord 類

在完成LoginForm後,我們還缺少一些東西,從使用者接受到資料了,那麼還需要從資料庫取出相應的資料來進行比較,所以我們接下來需要完成的是一個從資料庫獲取的資料的類——AR類,全稱是ActiveRecord,活動記錄類,方便用於查詢資料,只要類名和資料表的表名相同,那麼它就能從這個資料表中獲取資料,比如說這樣:

  1. <?php  
  2. namespace app\modules\backend\models;  
  3. use yii\db\ActiveRecord;  
  4. class User extends ActiveRecord{       } ?>  

此外,還能自己新增返回的表名,只要在這個類中重寫以下方法:

  1. public static function tableName(){  
  2.         return 'user';  
  3.     }  


(2)IdentityInterface 介面

一般來說,從資料庫查詢資料,只需要繼承AR類即可,但是,我們這個是使用者登入模型,核心是驗證,所以自然需要實現核心的驗證功能,就像LoginForm模型提到的validatePassword一樣,實際的驗證邏輯是在當前的User模型完成的。一般來說,實現IdentityInterface介面,需要實現以下方法:

  1. public static function findIdentity($id);  //①  
  2. public static function findIdentityByAccessToken($token, $type = null);   //②  
  3. public function getId();    //③  
  4. public function getAuthKey();   //④  
  5. public function validateAuthKey($authKey);    //⑤  

①findIdentity:是根據id查詢資料表對應的資料

②findIdentityByAccessToken是根據AccessToken(上文提到的)查詢對應的資料,而AccessToken我們在資料表也有這個欄位,那麼它到底有什麼用呢?其實AccessToken在我們當前的使用者登陸模型中用處並不大,它是專門用於Resetful登陸驗證用到的,具體可自行百度,這裡不展開說明。

③getId:返回當前AR類所對應的id

④getAuthKey:返回當前AR類所對應的auth_key

⑤validateAuthKey:這個方法比較重要,是我們後面要講到的cookie登陸驗證的核心所在。

好了,既然知道了這五個方法的用處,那麼我們在我們的User.php實現介面,然後重寫以上方法,完整的User.php的程式碼如下:

  1. <?php  
  2. namespace app\modules\backend\models;  
  3. use yii\db\ActiveRecord;  
  4. class User extends ActiveRecord implements \yii\web\IdentityInterface  
  5. {  
  6.     public static function tableName(){  
  7.         return 'user';  
  8.     }  
  9.     public static function findIdentity($id){  
  10.         return static::findOne($id);  
  11.     }  
  12.     public static function findIdentityByAccessToken($token,$type=null){  
  13.         return static::findOne(['accessToken'=>$token]);  
  14.     }  
  15.     public static function findByUsername($username){     //①  
  16.         return static::findOne(['username'=>$username]);   
  17.     }  
  18.     public function getId(){  
  19.         return $this->id;  
  20.     }  
  21.     public function getAuthkey(){  
  22.         return $this->auth_key;  
  23.     }  
  24.     public function validateAuthKey($authKey){  
  25.         return $this->auth_key === $authKey;  
  26.     }  
  27.     public function validatePassword($password){          //②  
  28.         return $this->password === md5($password);  
  29.     }  
  30.    <span style="white-space:pre"> </span> /** 
  31.     <span style="white-space:pre">    </span> * Generates "remember me" authentication key 
  32.     <span style="white-space:pre">    </span> */  
  33.         public function generateAuthKey()                    //③  
  34.         {  
  35.        <span style="white-space:pre">     </span>$this->auth_key = \Yii::$app->security->generateRandomString();  
  36.        <span style="white-space:pre">     </span>$this->save();  
  37.         }  
  38. }  
  39. ?>  

這裡分析其中的三個方法:

①findByUsername():在LoginForm的程式碼中,引用了這個方法,目的是根據使用者提交的username返回一個在資料表與username相同的資料項,即AR例項。

②validatePassword():這裡對使用者提交的密碼以及當前AR類的密碼進行比較。

③generateAuthKey():生成隨機的auth_key,用於cookie登陸。

到此,我們完成了Model的編寫,一共寫了兩個Model類:LoginForm和User,一個用於接收使用者提交的資料,一個用於獲取資料庫的資料,接下來我們編寫Controller.

三、控制器(Controller)

控制器,主要是用於資料的提交,把使用者提交的資料填充到相應的模型(Model)中,然後根據模型返回的資訊進一步渲染檢視(View),或者執行其他邏輯。

     這裡,把控制器命名為LoginController.php,以下是完整的實現程式碼:

  1. <?php  
  2. namespace app\controllers;  
  3. use Yii;  
  4. use yii\filters\AccessControl;  
  5. use yii\web\Controller;  
  6. use yii\filters\VerbFilter;  
  7. use app\models\LoginForm;  
  8. use app\models\ContactForm;  
  9. class SiteController extends Controller  
  10. {  
  11.     public function actionIndex()  
  12.     {  
  13.         return $this->render('index');  
  14.     }  
  15.     public function actionLogin()  
  16.     {  
  17.         if (!\Yii::