Yii框架登录流程分析
本文详细分析了Yii框架的登录流程。分享给大家供大家参考。具体分析如下:
Yii对于新手来说上手有点难度,特别是关于session,cookie和用户验证。现在我们就Yii中登录流程,来讲讲Yii开发中如何设置session,cookie和用户验证方面的一些通用知识
1.概述
Yii是一个全栈式的MVC框架,所谓全栈式指的是Yii框架本身实现了web开发中所要用到的所有功能,比如MVC,ORM(DAO/ActiveRecord),全球化(I18N/L10N),缓存(caching),基于jQueryAjax支持(jQuery-basedAJAXsupport),基于角色的用户验证(authenticationandrole-basedaccesscontrol),程序骨架生成器(scaffolding),输入验证(inputvalidation),窗体小部件(widgets),事件(events),主题(theming),web服务(Webservices),日志(logging)等功能.详见官方说明.
这里要说的只是Yii的登录流程.用Yii开发一般是用一个叫做Yiishell的控制台工具生成一个程序的骨架,这个骨架为我们分配好了按MVC方式开发web程序的基本结构,并且是一个可以直接运行的程序.如果你了解RubyonRails的话,原理是一样的.
2.网站登录流程
生成的程序中有一个protected目录,下面的controllers目录有个叫SiteController.php的文件,这个文件是自动生成的,里面有一个叫actionLogin的文件.程序登录流程默认就是从来开始的.Yii把类似于http://domain.com/index.php?r=site/login这样的地址通过叫router的组件转到上面说的actionLogin方法里的.这个路由的功能不是的这里说的重点.actionLogin方法的代码是这样的.
publicfunctionactionLogin(){ $model=newLoginForm; //collectuserinputdata if(isset($_POST['LoginForm'])){ $model->attributes=$_POST['LoginForm']; //validateuserinputandredirecttothepreviouspageifvalid if($model->validate()&&$model->login()) $this->redirect(Yii::app()->user->returnUrl); } //displaytheloginform $this->render('login',array('model'=>$model)); }
首先初始化一个LoginForm类,然后判断是否是用户点了登录后的请求(查看请求中有没有POST数据),如果是的话则先验证输入($model->validate)然后尝试登录($model->logiin),如果都成功,就跳转到登录前的页面,否则显示登录页面.
3.框架登录流程
LoginForm类继承于CFormModel,间接继承于CModel,所以他提供了CModel提供了一些像验证和错误处理方面的功能.其中的login方法就是执行验证操作的.方法首先通过用户提供的用户名和密码生成一个用来表示用户实体的UserIdentity类,该类中的authenticate方法执行实际的验证动作,比如从数据库中判断用户名和密码是否匹配.LoginForm类的login方法通过查询authenticate是否有错误发生来判断登录是否成功.如果成功则执行Yii::app()->user->login方法来使用户真正的的登录到系统中.前面讲到的这些流程是用户程序提供的,而Yii::app()->user->login也就是CWebUser的login方法是Yii框架提供的流程.我们来看看他做了些什么.下面是该方面的代码,位于(Yii)webauthCWebUser.php文件中.
publicfunctionlogin($identity,$duration=0){ $this->changeIdentity($identity->getId(),$identity->getName(),$identity->getPersistentStates()); if($duration>0){ if($this->allowAutoLogin) $this->saveToCookie($duration); else thrownewCException(Yii::t('yii','{class}.allowAutoLoginmustbesettrueinordertousecookie-basedauthentication.', array('{class}'=>get_class($this)))); } }
参数$identity是上面登录时生成的UserIdentity类,里面包含了基本的用户信息,如上面的Id,Name,还有可能是其它自定义的数据getPersistentStates.程序首先把$identity中的数据复制到CWebUser的实例中,这个过程包括了生成相应的session,其实主要目的是生成session.然后根据参数$duration(cookie保存的时间)和allowAutoLogin属性来判断是否生成可以用来下次自动登录的cookie.如果是则生成cookie(saveToCookie).
protectedfunctionsaveToCookie($duration){ $app=Yii::app(); $cookie=$this->createIdentityCookie($this->getStateKeyPrefix()); $cookie->expire=time()+$duration; $data=array( $this->getId(), $this->getName(), $duration, $this->saveIdentityStates(), ); $cookie->value=$app->getSecurityManager()->hashData(serialize($data)); $app->getRequest()->getCookies()->add($cookie->name,$cookie); }
首先是新建一个CHttpCookie,cookie的key通过getStateKeyPrefix方法取得,该方法默认返回md5('Yii.'.get_class($this).'.'.Yii::app()->getId());即类名和CApplication的Id,这个Id又是crc32函数生成的一个值.这个具体的值是多少无关紧要.但是每次都是产生一样的值的.接着设置expire,cookie的过期时间,再新建一个array,包含了基本数据,接着比较重要的是计算取得cookie的值,$app->getSecurityManager()->hashData(serialize($data)),getSecurityManager返回一个CSecurityManager的对象,并调用hashData方法.
publicfunctionhashData($data){ $hmac=$this->computeHMAC($data); return$hmac.$data; }
protectedfunctioncomputeHMAC($data){ if($this->_validation==='SHA1'){ $pack='H40'; $func='sha1'; } else{ $pack='H32'; $func='md5'; } $key=$this->getValidationKey(); $key=str_pad($func($key),64,chr(0)); return$func((str_repeat(chr(0x5C),64)^substr($key,0,64)).pack($pack,$func((str_repeat(chr(0x36),64)^substr($key,0,64)).$data))); }