YII学习,实现基于数据库的后台登录,用户体系.
上次研究Yii框架写到了要完成用户登陆系统.这次接着上次的写.
参考官方权威指南和参考手册,外加各种博客文章.历尽千辛万苦,数遍天上滴星星.完成了后台用户登录.这里用到了YII框架的
CUserIdentity类. -------实现基于用户名和密码的验证.
CWebForm类 ---------登录表单
CController类 ------控制器的基础类
CWebUser类 ------储存用户的持久身份信息
第一步:分离用户系统
要想实现用户登录.先研究一下YII的登录流程.YII框架提供和很有用的登录辅助类和用户身份类供我们使用.上次说到admin使用模块来开发管理后台.那么要实现后台与前台的相对独立.首先要实现前后台两套用户系统.YII的CUserIdentity类是用来提供用户身份相关的存储,验证等操作.这个类在默认的配置文件main.php里面有配置.就是配置文件里面components下面的user.查看手册可以看到这几行配置就是配置的CUserIdentity的一个实例.以后可以通过Yii::app()->user来访问这个实例.调用其中的方法.但是我想要两套用户系统.一套用作网站的前台登录.一套用作管理的后台登录.那么就要修改这个配置.用户系统是基于CUserIdentity类.那么如果我想要两套用户系统,就要有两个CUserIdentity的实例.也可以有两个CUserIdentity的子类.这样也能够实现用户系统的分离.我把其中用作前台登录的类叫做WebUsers类.这个类放在网站根目录下的components下面放置一个Webusers.php文件.里面只写明class WebUsers extends CWebUser{}就可以了.当我以后想修改网站用户的一些基础属性.只需要修改这个类就成了.另外一个类我放在admin模块下的components下面.同上面的一样只写明class AdminUsers extends CWebUser{}就可以了.在最后我在main里面配置一下.
修改原来user的声明改成以下的样子
'components'=>array( 'user'=>array( 'class'=>'WebUsers', 'stateKeyPrefix'=>'user',//设置前台session前缀 'allowAutoLogin'=>true, ), 'admin'=>array( // enable cookie-based authentication 'class'=>'AdminUsers', 'stateKeyPrefix'=>'admin',//设置后台session前缀 'allowAutoLogin'=>false, 'loginUrl' =>array('/admin/adminuser/login'), ),
之前上文说过.这个配置数组是用来配置类的.也就是说我分别对前台和后台的两个用于保存网站用户的实例进行了配置.经过这样的配置.我就能够通过Yii::app()->user访问到网站用户的相关属性和调用相关的方法.而通过Yii::app()->admin来访问网站后台管理用户的相关属性,并调用相关的方法.
第二步:完成控制器.
分离完了用户系统.就要写控制器了.因为CMS系统需要验证用户身份.也就是说没有正确的账号密码是不允许进行任何操作的.换个逻辑讲就是说在模块下的控制器的任何方法都应该在权限验证通过的情况下才能被执行.为了能够实现这个目的.我写了一个后台控制器的基础类,所有用作后台操作的控制器都继承自此控制器.这个控制器的名称我给定义成了AdminController.它继承自Ccontroller.我把这个类的文件存储在components下面,这样就会被模块的init方法自动导入进来.我可以通过手册发现在Ccontroller里面有一个方法叫做beforeAction().我只需要重写这个方法.在所有action执行之前验证用户身份.如果用户身份不合法.就直接引导到登录页面上去.这个是我们的实现思想.
小插曲:验证码.
想做登录,那么验证码这个关键的东西是不能够少的.在YII的框架中有提供验证码.但是使用起来比较让人费解.它说要实现验证码.我们需要重写控制器中的actions方法.完成一个继承自CCaptchaAction的action并且要求这个继承来的方法名字必须是captcha.这里我没有深入的研究,参考yii框架自动生成的联系页面上验证码的生成.我写了验证码.当然.验证码可以进行配置.只需要配置数组即可.
完成上述步骤后.我的后台管理用到的基础控制器就完成了.代码是下面这个样子的:
class AdminController extends CController { public $pageTitle="后台管理"; /** * @var string the default layout for the controller view. Defaults to '//layouts/column1', * meaning using a single column layout. See 'protected/views/layouts/column1.php'. */ public $layout='application.modules.admin.views.layouts.admin'; /** * @var array context menu items. This property will be assigned to {@link CMenu::items}. */ public $menu=array(); /** * @var array the breadcrumbs of the current page. The value of this property will * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links} * for more details on how to specify this property. */ public $breadcrumbs=array(); public function actions(){ return array( 'captcha'=>array( 'class'=>'CCaptchaAction', 'backColor'=>0xFFFFFF, ),); } protected function beforeAction($action){ if(Yii::app()->admin->isGuest&&$action->getId()!='login'&&$action->getId()!='captcha'){ $this->redirect(Yii::app()->admin->loginUrl); } return parent::beforeAction($action); } }
说一下这个beforeAction 第一行.我们判断我们之前声明的后台用户体系中的当前用户是不是来宾用户.并且判断当前的Action是不是为login或者captcha,也就是当前是不是在登录或者生成验证码.因为对于没有登录的用户,如果不允许登录操作和生成验证码操作就没办法完成登录. 第二行.如果条件成立.也就是说当前的用户没有登录.那么我们就调用控制器里面的重定向方法,将访问定向到我们之前再用户体系中声明的loginUrl去.最后一行.如果我们的用户登录了.那么我们就调用父类的这个方法.实际上父类的方法里面是直接去执行action了.
好了,我的基础控制器完成了,在这里我们定义了验证码,控制了用户登录.接下来我就要写后台用户的第一个控制器了,用户控制器.里面要实现的方法大概有(还没想好),用户登录,用户基本信息获取.用户登出.显示登录后台的首页.再想到再添加.于是完成了第一个控制器.AdminUserController,具体代码如下:
class AdminuserController extends AdminController { public function actionIndex() { $this->render('index'); } public function actionLogin() { $model=new AdminLoginForm; if(isset($_POST['AdminLoginForm'])) { $model->attributes=$_POST['AdminLoginForm'];//将用户输入块赋值给AdminLoginForm的实例 if($r = $model->validate()) { // form inputs are valid, do something here $this->actionIndex();//若登陆成功则显示首页 //$this->renderPartial('login',array('model'=>$model)); return; } } $this->renderPartial('login',array('model'=>$model)); } public function actionLogout(){ Yii::app()->admin->logout(); $this->actionLogin(); } }
代码贴出来了,先别着急看.这里面的actionLogin先不用去理解.我定义了三个动作.分别是login,logout,index,还有一个从父类继承过来的captcha.分别用来登录,登出,显示首页和显示验证码.
第三步,建立模型.
yii有很好的抽象结构.将数据抽象成两种模式,一种是用作表单的,这样的数据不被持久化保存.于是有表单模型.另外一种是用来抽象数据库的模型.这种数据会被保存到数据库里面.第二种模型里面封装了数据库的增删改查的方法.官方叫做CActiveRecord,我们可以理解为第二种模型为数据模型.第一种模型为表单模型.
表单模型,故名思议就是对表单的一个抽象.这个模型里面定义了表单的各个属性和字段.定义了表单的验证规则.等等.当渲染一个表单的时候实际上是生成了一个表单模型的实例.当我们填写一个表单并提交的时候实际上完成的操作应该是把用户输入的数据用来填充这个模型.并保证用户填充的数据能够满足这里的验证规则.也就是说我们对表单的操作实际上就是对这个表单模型实例的操作.按照Yii框架的设计原则,我们的表单模型都应该集成自表单模型的基类.而数据模型都应该集成自数据模型的基类.这两个基类分别是 CFormModel,CActiveRecord.
拿我们的登录表单来说.我们的登录表单继承自CFormModel,里面有几个属性.分别是用户名.密码.验证码.有一个方法,这个方法通过提交表单来收集用户输入并用已经定义的规则来比对用户输入.如果满足,就将用户输入赋值给表单模型实例的相应属性上,如果不满足就写入错误信息.(这个方法的名字叫做validate,是从CFormModel继承过来的//是用来验证的.)
数据模型.对应数据表的模型.可以理解为我们把数据库里面某一张表抽象为一个模型.这个表里面的每一行就是这个模型的一个实例.当我们执行这个数据模型的增删改查操作的时候,实际上就是对这个表的操作.而转换成面向对象的思想.我们就是在操作着数据对象.还拿我们的后台管理用户系统来说.我们需要实现对于后台用户表的抽象.于是我们有了一个后台用户表的数据模型类.继承自CActiveRecord.(可以用GII生成).我是用Gii生成的.
我的登陆表单模型如下:
<?php /** * ContactForm class. * ContactForm is the data structure for keeping * contact form data. It is used by the 'contact' action of 'SiteController'. */ class AdminLoginForm extends CFormModel { public $username; public $password; public $verifyCode;//验证码 private $_adminUserIdentity;//这个变量实际上是基于用户名和密码验证的类的实例 /** * Declares the validation rules. */ public function rules() { return array( array('verifyCode', 'captcha', 'allowEmpty'=>!CCaptcha::checkRequirements()), //当没有开启GD库拓展的时候允许验证码为空 array('username, password','required'), //用户名密码不能为空 // verifyCode needs to be entered correctly array('password', 'check_user'),//定义password需要使用该模型中的check_user方法来验证. ); } public function attributeLabels() { return array( 'username'=>'用户名', 'password'=>'密码', 'verifyCode'=>'验证码', ); } public function check_user($attribute,$params) { $this->_adminUserIdentity = new AdminUserIdentity($this->username,$this->password); $res = $this->_adminUserIdentity->authenticate();
//调用authenticate方法来验证用户提交的信息与数据库里面存储的信息是否一致. if($res == true){ Yii::app()->admin->login($this->_adminUserIdentity); return true; }else{ return false; } } }
第四步,理清逻辑.
先看看我们手里有什么,
有AdminuserController控制器.
有在main.php里面声明的admin用户体系,实际上是CWebUser的一个实例.
基于用户密码来验证的AdminUserIdentity.
有loginform的表单模型.(可以用gii来建立)
有admin_user数据表的数据模型.(可以用gii来建立)
当然,我们还要有一张存储用户数据的数据表.这个要自己在数据库里面建立了.这个就不写出来了.
我们需要一个能够渲染管理后台首页的视图.可能还要写一些layout.
还少什么:
少一个登录界面的视图.
那就来建立一个登录界面的视图.在yii权威指南里面可以找到使用表单那个章节.里面介绍了表单助手类的使用.这里我就不再说了.想要在前端展示验证码.可以参考例子程序.实际上<?php $this->widget('CCaptcha'); ?>就能够完成验证码的图片输出.this指的是控制器的实例,而widget值得是渲染一个挂件.当传递进入的参数为CCaptcha时就能够直接生成一个验证码.
上面提到的还有我的后台管理用户的用户体系文件,放上来如下:
<?php class AdminUserIdentity extends CUserIdentity { private $_id; public function authenticate() { $record = AdminUser::model()->findByAttributes(array('username'=>$this->username)); if($record===null) { $this->errorMessage = '用户名不存在'; $this->errorCode=true; } else if($record->password!==md5($this->password)) { $this->errorMessage = '密码不正确'; $this->errorCode=true; } else { $this->_id=$record->id; $this->setState('title', $record->username); $this->errorCode=false; } return !$this->errorCode; } public function getId() { return $this->_id; } }
到现在已经准备的差不多了,简单画了一下登录的流程.如下:
写到这里,就完成了基本的登录流程.当有用户来访问的时候我们就可以实现必须强制登录.其实我上面所说的东西,在官方的文档上也有很多描述,可以多啃啃手册.
上次我做了一个类比,把YII比作一个汽车工厂.这次.我们可以把后台这个模块比作一个大的生产车间.那么我们在main里面声明的AdminUsers类的实例就相当于一个管理这个生产车间的管理员,负责监管人员进入生产车间和离开生产车间,而AdminUserIdentity 这个基于用户名密码的验证类就相当于一个基于用户名密码的开门器.如果你没有相应的用户名密码.也不能够进入车间进行工作.
就写到这里.接下来要实现后台用户的管理.增删改查功能.如果你耐心的看完了这篇博文并觉得有收货的话,请帮忙赞一下.