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,以下為程式碼:
- <?php
- namespace app\modules\backend\models;
- use Yii;
- use yii\base\Model;
- /**
-
* LoginForm is the model behind the login form.
- */
- class LoginForm extends Model
- {
- public $username;
- public $password;
- public $rememberMe = true;
- private $_user = false;
- /**
- * @return array the validation rules.
- */
- public function rules()<span style="white-space:pre"> </span>//①
- {
- return [
- // username and password are both required
- [['username', 'password'], 'required','message'=>""],
- // rememberMe must be a boolean value
- ['rememberMe', 'boolean'],
- // password is validated by validatePassword()
- ['password', 'validatePassword'],
- ];
- }
- /**
- * Validates the password.
- * This method serves as the inline validation for password.
- *
- * @param string $attribute the attribute currently being validated
- * @param array $params the additional name-value pairs given in the rule
- */
- public function validatePassword($attribute, $params)
- {
- if (!$this->hasErrors()) {
- $user = $this->getUser();
- if (!$user || !$user->validatePassword($this->password)) {
- $this->addError($attribute, 'Incorrect username or 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()) {
- if($this->rememberMe)
- {
- $this->_user->generateAuthKey();//③
- }
- return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
- }
- return false;
- }
- /**
- * Finds user by [[username]]
- *
- * @return User|null
- */
- public function getUser()
- {
- if ($this->_user === false) {
- $this->_user = User::findByUsername($this->username); //②
- }
- return $this->_user;
- }
- }
該Model是根據basic模板自帶的LoginForm修改而成,程式碼中大多有註釋,這裡關注以下程式碼:
①號程式碼處是rules規則,rules規則定義了填充過來的資料的規則,驗證所填的資料是否為空,是否符合格式之類的,其中有一欄是password,對應的規則是validatePassword,會自動呼叫當前類的validatePassword()方法,注意與下文的User類對應的方法區分。
②號程式碼,呼叫了User類裡面的findByUsername方法,這個User類下面會寫到,主要是為了返回一個AR類例項,與當前LoginForm的資料進行比較。
③號程式碼,這裡暫時不提,等講到cookie登陸的時候再提。
2、User.php
(1)ActiveRecord 類
在完成LoginForm後,我們還缺少一些東西,從使用者接受到資料了,那麼還需要從資料庫取出相應的資料來進行比較,所以我們接下來需要完成的是一個從資料庫獲取的資料的類——AR類,全稱是ActiveRecord,活動記錄類,方便用於查詢資料,只要類名和資料表的表名相同,那麼它就能從這個資料表中獲取資料,比如說這樣:
- <?php
- namespace app\modules\backend\models;
- use yii\db\ActiveRecord;
- class User extends ActiveRecord{ } ?>
此外,還能自己新增返回的表名,只要在這個類中重寫以下方法:
- public static function tableName(){
- return 'user';
- }
(2)IdentityInterface 介面
一般來說,從資料庫查詢資料,只需要繼承AR類即可,但是,我們這個是使用者登入模型,核心是驗證,所以自然需要實現核心的驗證功能,就像LoginForm模型提到的validatePassword一樣,實際的驗證邏輯是在當前的User模型完成的。一般來說,實現IdentityInterface介面,需要實現以下方法:
- public static function findIdentity($id); //①
- public static function findIdentityByAccessToken($token, $type = null); //②
- public function getId(); //③
- public function getAuthKey(); //④
- public function validateAuthKey($authKey); //⑤
①findIdentity:是根據id查詢資料表對應的資料
②findIdentityByAccessToken是根據AccessToken(上文提到的)查詢對應的資料,而AccessToken我們在資料表也有這個欄位,那麼它到底有什麼用呢?其實AccessToken在我們當前的使用者登陸模型中用處並不大,它是專門用於Resetful登陸驗證用到的,具體可自行百度,這裡不展開說明。
③getId:返回當前AR類所對應的id
④getAuthKey:返回當前AR類所對應的auth_key
⑤validateAuthKey:這個方法比較重要,是我們後面要講到的cookie登陸驗證的核心所在。
好了,既然知道了這五個方法的用處,那麼我們在我們的User.php實現介面,然後重寫以上方法,完整的User.php的程式碼如下:
- <?php
- namespace app\modules\backend\models;
- use yii\db\ActiveRecord;
- class User extends ActiveRecord implements \yii\web\IdentityInterface
- {
- public static function tableName(){
- return 'user';
- }
- public static function findIdentity($id){
- return static::findOne($id);
- }
- public static function findIdentityByAccessToken($token,$type=null){
- return static::findOne(['accessToken'=>$token]);
- }
- public static function findByUsername($username){ //①
- return static::findOne(['username'=>$username]);
- }
- public function getId(){
- return $this->id;
- }
- public function getAuthkey(){
- return $this->auth_key;
- }
- public function validateAuthKey($authKey){
- return $this->auth_key === $authKey;
- }
- public function validatePassword($password){ //②
- return $this->password === md5($password);
- }
- <span style="white-space:pre"> </span> /**
- <span style="white-space:pre"> </span> * Generates "remember me" authentication key
- <span style="white-space:pre"> </span> */
- public function generateAuthKey() //③
- {
- <span style="white-space:pre"> </span>$this->auth_key = \Yii::$app->security->generateRandomString();
- <span style="white-space:pre"> </span>$this->save();
- }
- }
- ?>
這裡分析其中的三個方法:
①findByUsername():在LoginForm的程式碼中,引用了這個方法,目的是根據使用者提交的username返回一個在資料表與username相同的資料項,即AR例項。
②validatePassword():這裡對使用者提交的密碼以及當前AR類的密碼進行比較。
③generateAuthKey():生成隨機的auth_key,用於cookie登陸。
到此,我們完成了Model的編寫,一共寫了兩個Model類:LoginForm和User,一個用於接收使用者提交的資料,一個用於獲取資料庫的資料,接下來我們編寫Controller.
三、控制器(Controller)
控制器,主要是用於資料的提交,把使用者提交的資料填充到相應的模型(Model)中,然後根據模型返回的資訊進一步渲染檢視(View),或者執行其他邏輯。
這裡,把控制器命名為LoginController.php,以下是完整的實現程式碼:
- <?php
- namespace app\controllers;
- use Yii;
- use yii\filters\AccessControl;
- use yii\web\Controller;
- use yii\filters\VerbFilter;
- use app\models\LoginForm;
- use app\models\ContactForm;
- class SiteController extends Controller
- {
- public function actionIndex()
- {
- return $this->render('index');
- }
- public function actionLogin()
- {
-
if (!\Yii::