原地址:http://blog.csdn.net/a553181867/article/details/50987388

最近在利用Yii 2.0框架进行项目后台的编写,遇到的第一个问题是用户登陆,包括利用cookie,session登陆等等,笔者从源码角度结合实例为各位详细解析如何编写一个完整的用户登陆模块。(笔者的本地环境是PHP 5.5+MySQL5.6)

一、准备


开始编写代码之前,我们需要思考一下:用户登陆模块,实现的是什么功能?很明显,是登陆功能,那么,登陆需要用户名和密码,我们在数据库的一张表中就应该
准备好用户名和密码的字段,再思考一下,如果要实现自动登陆的功能,那么还需要什么?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::$app->user->isGuest) {     //①
  18. return $this->goHome();
  19. }
  20. $model = new LoginForm();             //②
  21. if ($model->load(Yii::$app->request->post()) && $model->login()) {      //③
  22. return $this->goBack();          //④
  23. }
  24. return $this->render('login', [      //⑤
  25. 'model' => $model,
  26. ]);
  27. }
  28. public function actionLogout()
  29. {
  30. Yii::$app->user->logout();
  31. return $this->goHome();
  32. }
  33. }

关注其中的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快速生成表单,代码如下:

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

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

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

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

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

ActiveForm::end()    :   结束表单

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

yiii 框架登录 判断是否是游客模式及未登录状态的更多相关文章

  1. swift项目实战--微博的未登录界面的实现,和监听未登录界面两个按钮的两种实现方法

    1.未登录界面的实现 微博项目中,用户不登录的话,显示的是未登录的界面.项目中TabBarVC的子控制器都是tableViewVC,所以抽取了父类,让父类判断用户是否登录,决定显示什么样的界面.loa ...

  2. 用户未登录或Session超时时重定向到登录页,不那么简单

    在网站开发中,我们经常有这样的场景出现: 情景1:对未登录的用户或没有权限的用户,当其想访问某个受限网页时,系统要能够自动转到登录页面.   情景2:对于用session保存用户状态的情况还有这样一种 ...

  3. 第二百六十九节,Tornado框架-Session登录判断

    Tornado框架-Session登录判断 Session需要结合cookie来实现 Session的理解 1.用户登录系统时,服务器端获取系统当前时间,进行nd5加密,得到加密后的密串 2.将密串作 ...

  4. 苹果手机Safari无痕浏览模式下系统登录成功但是页面不跳转

    昨天下午,测试提了一个bug,问题是:在苹果手机Safari无痕浏览模式下系统登录成功但是页面不跳转. 思前想后找了半天没思路,后来经过同事的点拨,说可能是禁用了cookie之类的,反正我也没思路就顺 ...

  5. spring MVC使用Interceptor做用户登录判断

    在任何一个项目中,我们必须要用到的就是用户登录,那么就少不了用户是否登录的判断,如果我们每一个请求都要去做一次判断,那么就会变得很麻烦,但我们复制粘贴的时候我们就要考虑我们的代码写的是不是有问题,是不 ...

  6. 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...

  7. ubuntu不能登录图形用户界面,游客身份可登陆,命令行可登陆

    ubuntu是13.04版本,我猜其他的版本解决办法大概也一样.当开机进入登陆界面后我们输入密码后并没有进入应该进入的图形用户界面,而是进入一个命令行界面并且一闪而过又回到了登录界面,而已游客的身份却 ...

  8. 【转】【可用】Android 登录判断器,登录成功后帮你准确跳转到目标activity

    我们在使用应用时肯定遇到过这样的情景,打开应用,并不是需要我们登录,你可以浏览应用中的大部分页面,但是当你想看某个详情页的时候,点击后突然跳转到了登录页面,好,我们输入账号密码,点击登录,登录成功,跳 ...

  9. Loadrunner登录判断

    loadrunner判断登录是否成功,以下方法可以解决:1.利用添加检查点web_reg_find函数的方法---------------------------- lr_start_transact ...

随机推荐

  1. UVALive6900 Road Repair(树的点分治)

    题目大概说一棵树,树边有费用和收益两个属性,求一条收益和最大的路径满足费用和不超过C. 树上任意两点的路径都可以看成是过某一个子树根的路径,显然树分治. 治的时候要解决的一个问题是,找到费用小于等于某 ...

  2. 【SAP BusinessObjects】WEBI中的动态求和,累加函数的使用

    在WEBI中,提供了这样一个函数: RunningSum([字段名]) 其作用是,将[字段名]这一列进行累加动态求和 对于需要进行计算累加值的列就不必写复杂的SQL,直接使用此函数即可解决.

  3. BZOJ3548 : [ONTAK2010]Party

    首先将朋友通过并查集缩起来,因为$P\geq\frac{n(n-1)}{3}$,所以最后最多剩下$46$个点. 将自相矛盾的点删掉,就变成求最大权独立集问题,这等于求补图的最大团. 然后直接用Bron ...

  4. OpenResty 简单编写一个Module

    使用 Lua module 来进行 Lua 代码的复用是推荐的做法.然后在用户代码中直接用require()来调用 module代码: local myTest = {} function myTes ...

  5. ACM: I Hate It 解题报告 - 线段树

    I Hate It Time Limit:3000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Des ...

  6. [总结]HNOI2015省队选拔

    // 此博文为迁移而来,写于2015年4月21日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vy9t.html 这次省 ...

  7. Js C# 实现跨域访问数据

    使用项目一的js调用项目二的数据 1.项目一 @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta ...

  8. SQL 联合索引 与 单一列的索引 比较

    背景: 公司业务迅速扩展,很多网站.接口都因为大流量的数据,发生服务器习惯性死机:一条sql查询语句只能适用于一定的网络环境,没有优化的查询当遇上大数据时就不适用了. 本文主旨: 讨论什么情况下能利用 ...

  9. Find a way——L

    L. Find a way Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at finally. Leave N ...

  10. Oracle求部门员工工资占总工资的比率

    --根据每个部门来统计部门工资总和 select deptid, sum(sal) 工资合计 from emp group by deptid; --根据每个部门来统计部门工资总和select dep ...