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

    该Model是根据basic模板自带的LoginForm修改而成,代码中大多有注释,这里关注以下代码:

    ①号代码处是rules规则,rules规则定义了填充过来的数据的规则,验证所填的数据是否为空,是否符合格式之类的,其中有一栏是password,对应的规则是validatePassword,会自动调用当前类的validatePassword()方法,注意与下文的User类对应的方法区分。

    ②号代码,调用了User类里面的findByUsername方法,这个User类下面会写到,主要是为了返回一个AR类实例,与当前LoginForm的数据进行比较。

    ③号代码,这里暂时不提,等讲到cookie登陆的时候再提。


    2、User.php

    (1)ActiveRecord 类

    在完成LoginForm后,我们还缺少一些东西,从用户接受到数据了,那么还需要从数据库取出相应的数据来进行比较,所以我们接下来需要完成的是一个从数据库获取的数据的类——AR类,全称是ActiveRecord,活动记录类,方便用于查找数据,只要类名和数据表的表名相同,那么它就能从这个数据表中获取数据,比如说这样:

    [php] view plain copy
     
     print?
    1. <?php  
    2. namespace appmodulesackendmodels;  
    3. use yiidbActiveRecord;  
    4.   
    5. class User extends ActiveRecord{       } ?>  

    此外,还能自己添加返回的表名,只要在这个类中重写以下方法:

    [php] view plain copy
     
     print?
    1. public static function tableName(){  
    2.         return 'user';  
    3.     }  


    (2)IdentityInterface 接口

    一般来说,从数据库查找数据,只需要继承AR类即可,但是,我们这个是用户登录模型,核心是验证,所以自然需要实现核心的验证功能,就像LoginForm模型提到的validatePassword一样,实际的验证逻辑是在当前的User模型完成的。一般来说,实现IdentityInterface接口,需要实现以下方法:

    [php] view plain copy
     
     print?
    1. public static function findIdentity($id);  //①  
    2.   
    3. public static function findIdentityByAccessToken($token, $type = null);   //②  
    4.   
    5. public function getId();    //③  
    6.   
    7. public function getAuthKey();   //④  
    8.   
    9. 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] view plain copy
     
     print?
    1. <?php  
    2. namespace appmodulesackendmodels;  
    3. use yiidbActiveRecord;  
    4.   
    5. class User extends ActiveRecord implements yiiwebIdentityInterface  
    6. {  
    7.   
    8.     public static function tableName(){  
    9.         return 'user';  
    10.     }  
    11.   
    12.     public static function findIdentity($id){  
    13.         return static::findOne($id);  
    14.     }  
    15.   
    16.     public static function findIdentityByAccessToken($token,$type=null){  
    17.         return static::findOne(['accessToken'=>$token]);  
    18.     }  
    19.   
    20.     public static function findByUsername($username){     //①  
    21.         return static::findOne(['username'=>$username]);   
    22.     }  
    23.   
    24.     public function getId(){  
    25.         return $this->id;  
    26.     }  
    27.   
    28.     public function getAuthkey(){  
    29.         return $this->auth_key;  
    30.     }  
    31.   
    32.     public function validateAuthKey($authKey){  
    33.         return $this->auth_key === $authKey;  
    34.     }  
    35.   
    36.     public function validatePassword($password){          //②  
    37.         return $this->password === md5($password);  
    38.     }  
    39.   
    40.    <span style="white-space:pre"> </span> /** 
    41.     <span style="white-space:pre">    </span> * Generates "remember me" authentication key 
    42.     <span style="white-space:pre">    </span> */  
    43.         public function generateAuthKey()                    //③  
    44.         {  
    45.        <span style="white-space:pre">     </span>$this->auth_key = Yii::$app->security->generateRandomString();  
    46.        <span style="white-space:pre">     </span>$this->save();  
    47.         }  
    48.   
    49. }  
    50. ?>  

    这里分析其中的三个方法:

    ①findByUsername():在LoginForm的代码中,引用了这个方法,目的是根据用户提交的username返回一个在数据表与username相同的数据项,即AR实例。

    ②validatePassword():这里对用户提交的密码以及当前AR类的密码进行比较。

    ③generateAuthKey():生成随机的auth_key,用于cookie登陆。

    到此,我们完成了Model的编写,一共写了两个Model类:LoginForm和User,一个用于接收用户提交的数据,一个用于获取数据库的数据,接下来我们编写Controller.

    三、控制器(Controller)

    控制器,主要是用于数据的提交,把用户提交的数据填充到相应的模型(Model)中,然后根据模型返回的信息进一步渲染视图(View),或者执行其他逻辑。

         这里,把控制器命名为LoginController.php,以下是完整的实现代码:

    1. <?php  
    2.   
    3. namespace appcontrollers;  
    4.   
    5. use Yii;  
    6. use yiifiltersAccessControl;  
    7. use yiiwebController;  
    8. use yiifiltersVerbFilter;  
    9. use appmodelsLoginForm;  
    10. use appmodelsContactForm;  
    11.   
    12. class SiteController extends Controller  
    13. {  
    14.     public function actionIndex()  
    15.     {  
    16.         return $this->render('index');  
    17.     }  
    18.   
    19.     public function actionLogin()  
    20.     {  
    21.         if (!Yii::$app->user->isGuest) {     //①  
    22.             return $this->goHome();  
    23.         }  
    24.   
    25.         $model = new LoginForm();             //②  
    26.         if ($model->load(Yii::$app->request->post()) && $model->login()) {      //③  
    27.             return $this->goBack();          //④  
    28.         }  
    29.         return $this->render('login', [      //⑤  
    30.             'model' => $model,  
    31.         ]);  
    32.     }  
    33.   
    34.     public function actionLogout()  
    35.     {  
    36.         Yii::$app->user->logout();  
    37.   
    38.         return $this->goHome();  
    39.     }  
    40. }  

    关注其中的actionLogin()方法:

    ①首先从Yii::$app->user->isGuest中判断,当前是否是游客模式,即未登陆状态,如果用户已经登陆,会在user类中储存当前登陆用户的信息。

    ②如果当前是游客,会先实例化一个LoginForm模型

    ③这行代码是整个login方法的核心所在,首先:$model->load(Yii::$app->request->post())把post过来的数据填充进$model,即LoginForm模型,如果返回true,则填充成功。接着:$model->login():执行LoginForm类里面的login()方法,可以从login()方法里面看到,将会执行一系列的验证。

    关于Yii框架到底是怎样进行用户登陆的,底层是怎样实现的,我们在下一篇文章详谈,这里先说明实现方法。

    四、视图(View)

    在实现了model和controller,接下来是视图部分,由于用户需要输入数据,所以我们要提供一个表单,在Yii2中,提供了ActiveForm快速生成表单,代码如下:

    [php] view plain copy
     
     print?
    1. <?php  
    2.   
    3. /* @var $this yiiwebView */  
    4. /* @var $form yiiootstrapActiveForm */  
    5. /* @var $model appmodelsLoginForm */  
    6.   
    7. use yiihelpersHtml;  
    8. use yiiootstrapActiveForm;  
    9.   
    10. $this->title = 'Login';  
    11. $this->params['breadcrumbs'][] = $this->title;  
    12. ?>  
    13. <div class="site-login">  
    14.     <h1><?= Html::encode($this->title) ?></h1>  
    15.   
    16.     <p>Please fill out the following fields to login:</p>  
    17.   
    18.     <?php $form = ActiveForm::begin([  
    19.         'id' => 'login-form',  
    20.         'options' => ['class' => 'form-horizontal'],  
    21.         'fieldConfig' => [  
    22.             'template' => "{label} <div class="col-lg-3">{input}</div> <div class="col-lg-8">{error}</div>",  
    23.             'labelOptions' => ['class' => 'col-lg-1 control-label'],  
    24.         ],  
    25.     ]); ?>  
    26.   
    27.         <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>  
    28.   
    29.         <?= $form->field($model, 'password')->passwordInput() ?>  
    30.   
    31.         <?= $form->field($model, 'rememberMe')->checkbox([  
    32.             'template' => "<div class="col-lg-offset-1 col-lg-3">{input} {label}</div> <div class="col-lg-8">{error}</div>",  
    33.         ]) ?>  
    34.   
    35.         <div class="form-group">  
    36.             <div class="col-lg-offset-1 col-lg-11">  
    37.                 <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>  
    38.             </div>  
    39.         </div>  
    40.   
    41.     <?php ActiveForm::end(); ?>  
    42.   
    43.     <div class="col-lg-offset-1" style="color:#999;">  
    44.         You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>  
    45.         To modify the username/password, please check out the code <code>appmodelsUser::$users</code>.  
    46.     </div>  
    47. </div>  

    $form=ActiveForm::begin() :创建一个Form表单

    $form=field()->textInput()   :创建一个文本输入框

    $form=field()->checkbox() :创建一个checkbox

    Html::submitButton():          创建一个登陆按钮

    ActiveForm::end()    :   结束表单

    以上,就是创建一个用户登陆模块的全流程,这里对用户登陆的细节和怎样实现cookie自动登陆只是一笔带过,更详细的源码分析请看下一篇博文,谢谢。

    ps:本文转自他人,并非原创

  • 相关阅读:
    Linux中常用操作命令
    JQuery Each循环遍历每个元素
    get set
    Launch Screen在iOS7/8中的实现
    程序猿必备的Git教程
    浏览器的工作原理:新式网络浏览器幕后揭秘
    浏览器的工作原理:新式网络浏览器幕后揭秘
    游览器中javascript的执行过程
    游览器中javascript的执行过程
    浅析 Cordova for iOS
  • 原文地址:https://www.cnblogs.com/chenhaoyu/p/5949488.html
Copyright © 2020-2023  润新知