【Yii】Authentication (1)
大多数网站都有身份验证系统,用户必须登录才能发表文章、评论内容甚至管理网站等等。无论如何,在用户进行任何操作前对其身份的检验是必须的。Yii框架自动生成的网站中使用内嵌的验证程序,下面三个文件会自动生成,用来管理验证:
LoginForm.php定义相应的规则和行为login.php即登录时的form本身,UserIdentity.php定义了执行实际验证功能的model。当然,protected/controllers/SiteController.php中的部分代码使得action可以执行,即controller。
UserIdentity.php中是身份验证的核心内容,默认的代码如下:
public function authenticate() { $users=array( // username => password 'demo'=>'demo', 'admin'=>'admin', ); if(!isset($users[$this->username])) $this->errorCode=self::ERROR_USERNAME_INVALID; else if($users[$this->username]!==$this->password) $this->errorCode=self::ERROR_PASSWORD_INVALID; else $this->errorCode=self::ERROR_NONE; return !$this->errorCode; }
这里采用硬编码将用户名称和密码写进方法里,当然,绝大多数网站不可能这么做,身份验证基本上都要跟数据库联系起来。那么该如何通过数据库中的数据来验证呢?
在这之前,我们先来看看Yii中实现身份验证的流程。
当我们点击login时,浏览器将跳转到www.example.com/index.php/site/login,因为Yii将actionLogin()设为SiteController的默认执行方法。actionLogin()方法如下:
public function actionLogin() { $form=new LoginForm; if(isset($_POST['LoginForm'])) { $form->attributes=$_POST['LoginForm']; // validate user input and redirect to previous page if valid if($form->validate() && $form->login()) $this->redirect(Yii::app()->user->returnUrl); } // display the login form $this->render('login',array('form'=>$form)); }
首先,一个LoginForm的对象$form被创建,该类由Model类LoginForm定义。如果用户登陆的数据被提交,那么$form立刻收集数据并且进行验证。如果通过,页面将跳转到用户之前所在的页面。如果没有通过,那么login form和附带的错误信息将被显示。
上面的validate()方法意味着数据必须通过Model类LoginForm中的rules()规则,即:
public function rules() { return array( array('username, password', 'required'), array('password', 'authenticate'), ); }
其中username、password都必须填写,并且password要通过authenticate()方法的验证,这个方法被定义在LoginForm中,即:
public function authenticate($attribute,$params) { if(!$this->hasErrors()) { $this->_identity=new UserIdentity($this->username,$this->password); if(!$this->_identity->authenticate()) $this->addError('password','Incorrect username or password.'); } }
当没有input errors时,一个UserIdentity的对象被创建,username、password被传入构造函数。Useridentity对象的authenticate()方法被调用。
整个身份验证的过程大致如下图所示:
接下来我们要实现通过数据库来进行身份验证。
先从最底层改起,修改UserIdentity中的authenticate()方法如下:
public function authenticate() { $user = User::model()->findByAttributes(array('username'=>$this->username)); if ($user===null) { $this->errorCode=self::ERROR_USERNAME_INVALID; } else if ($user->password!== SHA1($this->password) ) { $this->errorCode=self::ERROR_PASSWORD_INVALID; } else { // Okay! $this->errorCode=self::ERROR_NONE; } return !$this->errorCode; }
其中通过Model类User查询username匹配的条目,返回一个User对象,如果返回对象为空,那验证失败,将errorCode赋值。如果密码不匹配,同样验证也失败(此处使用SHA1加密算法)。如果一切正常,那么errorCode赋值ERROR_NONE。
接着修改Model类LoginForm中的authenticate():
public function authenticate($attribute,$params) { if(!$this->hasErrors()) // we only want to authenticate when no input errors { $identity=new UserIdentity($this->username,$this->password); $identity->authenticate(); switch($identity->errorCode) { case UserIdentity::ERROR_NONE: $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days Yii::app()->user->login($identity,$duration); break; case UserIdentity::ERROR_USERNAME_INVALID: $this->addError('username','Username is incorrect.'); break; default: // UserIdentity::ERROR_PASSWORD_INVALID $this->addError('password','Password is incorrect.'); break; } } }
这里调用UserIdentity中的authenticate()方法,通过产生的errorCode来判断该执行何种操作。
至此,便完成了使用数据库中的用户信息进行身份验证的功能。更复杂的身份验证将在【Yii】Authentication (2) 中讨论