yii2项目实战-用户管理之登录与注册功能实现
更新于 2016年10月14日 by 白狼 被浏览了 3277 次

上一章节我们讲述了如何通过新建数据模型来配置yii2的user组件,但是课后有小伙伴发来问卷,为啥在创建user_backend数据表的时候销毁了 password_reset_token 字段呢?其实这个字段对后台管理基本没啥子用,你要是非要,也可以,这里只对该字段以及用途做一个说明,课后需要的可以自行实现。

该字段具有唯一性,其用途在于用户找回密码。且该字段具有时效性,过期时间参考common\config\params.php文件的user.passwordResetTokenExpire项配置,过期时间默认是1小时。注意哦,默认的找回密码是基于邮件且发送的链接内容包含该字段,用于点击链接跳转后可根据该字段获取到具体用户并实现用户密码的修改。因为看起来还是蛮繁琐的,如果你没听懂,可以在小站注册一个帐号,通过邮件找回密码测试一番。

因此,考虑到我们后台,无需真么繁琐,后面我们增加一个密码重置的功能,让用于自行修改密码即可。

说了那么多,我们接着上一章节,来说一说如何通过新建的数据表以及user组件来创建一个新的用户,以及如何实现新用户的登录机制。

其实也是so easy的,跟着步骤走,操作起来简单易学,一学就会,简直不能再6!

新用户的创建

打开 index.php?r=user-backend 页面,我们发现有一个创建的按钮,当然,如果我们真的这么创建了,创建的用户肯定是废的,因为肯定不能登录嘛!至少密码我们这里没有加密吧,对不对?

接下来我们就来实现添加一个新用户的操作,然后一并实现其登陆的操作。

①、我们将【创建】的按钮改为【添加新用户】,并修改其对应的链接地址

<?= Html::a('添加新用户', ['signup'], ['class' => 'btn btn-success']) ?>

可以看到这里我们命名当前控制器的signup操作为添加新用户的操作,接着我们就去实现掉这个操作。

②、实现signup方法,注意哦,我们需要实现两步,分别是渲染添加用户的表单和处理表单提交的数据。

/**
 *  create new user
 */
public function actionSignup ()
{
    $model = new \backend\models\SignupForm();

    // 如果是post提交且有对提交的数据校验成功(我们在SignupForm的signup方法进行了实现)
    // $model->load() 方法,实质是把post过来的数据赋值给model
    // $model->signup() 方法, 是我们要实现的具体的添加用户操作
    if ($model->load(Yii::$app->request->post()) && $model->signup()) {
        return $this->redirect(['index']);
    }

    // 渲染添加新用户的表单
    return $this->render('signup', [
        'model' => $model,
    ]);
}

③、我们来看下渲染的表单文件signup.php,默认的视图文件存放在 views/user-backend/ 目录下,可以看到我们的表单页面是用bootstrap构建的,这样样式问题就不需要担心了。

<?php


/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \backend\models\SignupForm */


use yii\helpers\Html;
use yii\bootstrap\ActiveForm;


$this->title = '添加新用户';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-signup">
    <div class="row">
        <div class="col-lg-5">
            <?php $form = ActiveForm::begin(['id' => 'form-signup']); ?>

                <?= $form->field($model, 'username')->label('登陆名')->textInput(['autofocus' => true]) ?>

                <?= $form->field($model, 'email')->label('邮箱') ?>

                <?= $form->field($model, 'password')->label('密码')->passwordInput() ?>

                <div class="form-group">
                    <?= Html::submitButton('添加', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
                </div>

            <?php ActiveForm::end(); ?>
        </div>
    </div>
</div>

④、从第②、③步来看,我们紧接着要实现的是Signupform的signup方法,该方法的含义是把接收到的表单数据进行过滤验证然后入库操作。你不可能直接就把用户提交过来的数据直接入库了,比方说邮箱不合法呢?对表单的认证在yii2中操作起来是非常方便的,是需要配置model的rules规则即可。那有些人要问了,我的规则配置好了,什么时候校验呢?这个很简单,你只需要在数据入库之前调用一下model的validate方法或者save方法(save方法内部会调用validate方法)即可。

<?php
namespace backend\models;
use yii\base\Model;
use backend\models\UserBackend;


/**
 * Signup form
 */
class SignupForm extends Model
{
    public $username;
    public $email;
    public $password;
    
    public $created_at;
    public $updated_at;

    /**
     * @inheritdoc
     * 对数据的校验规则
     */
    public function rules()
    {
        return [
            // 对username的值进行两边去空格过滤
            ['username', 'filter', 'filter' => 'trim'],
            
            // required表示必须的,也就是说表单提交过来的值必须要有, message 是username不满足required规则时给的提示消息
            ['username', 'required', 'message' => '用户名不可以为空'],
            
            // unique表示唯一性,targetClass表示的数据模型 这里就是说UserBackend模型对应的数据表字段username必须唯一
            ['username', 'unique', 'targetClass' => '\backend\models\UserBackend', 'message' => '用户名已存在.'],

            // string 字符串,这里我们限定的意思就是username至少包含2个字符,最多255个字符
            ['username', 'string', 'min' => 2, 'max' => 255],

            // 下面的规则基本上都同上,不解释了

            ['email', 'filter', 'filter' => 'trim'],
            ['email', 'required', 'message' => '邮箱不可以唯恐'],
            ['email', 'email'],
            ['email', 'string', 'max' => 255],
            ['email', 'unique', 'targetClass' => '\backend\models\UserBackend', 'message' => 'email已经被设置了.'],
            ['password', 'required', 'message' => '密码不可以为空'],
            ['password', 'string', 'min' => 6, 'tooShort' => '密码至少填写6位'],   
            // default 默认在没有数据的时候才会进行赋值
            [['created_at', 'updated_at'], 'default', 'value' => date('Y-m-d H:i:s')],
        ];
    }
    /**
     * Signs user up.
     *
     * @return true|false 添加成功或者添加失败
     */
    public function signup()
    {
        // 调用validate方法对表单数据进行验证,验证规则参考上面的rules方法
        if (!$this->validate()) {
            return null;
        }

        // 实现数据入库操作
        $user = new UserBackend();
        $user->username = $this->username;
        $user->email = $this->email;
        $user->created_at = $this->created_at;   
        $user->updated_at = $this->updated_at;

        // 设置密码,密码肯定要加密,暂时我们还没有实现,看下面我们有实现的代码
        $user->setPassword($this->password);

        // 生成 "remember me" 认证key
        $user->generateAuthKey();

        // save(false)的意思是:不调用UserBackend的rules再做校验并实现数据入库操作
        // 这里这个false如果不加,save底层会调用UserBackend的rules方法再对数据进行一次校验,因为我们上面已经调用Signup的rules校验过了,这里就没必要在用UserBackend的rules校验了
        return $user->save(false);
    }
}

⑤、我们在SignupForm的signup操作中看到,需要实现UserBackend的setPassword和generateAuthKey方法,我们打开backend\models\UserBackend.php文件新增下面两个方法

    /**
     * 为model的password_hash字段生成密码的hash值
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }

    /**
     * 生成 "remember me" 认证key
     */
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }

现在我们添加新用户的界面是下面这样的

0bd365a984-yii2-user.png

接着我们测试下添加一个新用户test1,发现也是可以的。

f6a0462bab-yii2-user2.png

暂停一下,说了那么多,我们先回过头来缕缕实现的逻辑

ce8fb4e327-blogadduser.png

登录的实现

从上面的操作中,我们看到,已经可以成功添加新用户了,接下来就是关键时刻,我们要让新用户可以登录系统!

想想添加新用户的流程实现,我们总结出实现登录功能至少需要

①、创建一个登录的表单

②、创建一个登录校验的模型model

③、接收登录表单数据并校验过滤,然后不是入库了,是校验密码,正确后,登录的信息存session

可喜的是,上面三个小步骤yii基本上都帮我们实现了,实现了,实现了!我们接下来的工作主要就是简单分析一下登录的流程

看①、我们打开SiteController文件,找到actionLogin操作

public function actionLogin()
{
    // 判断用户是访客还是认证用户 
    // isGuest为真表示访客,isGuest非真表示认证用户,认证过的用户表示已经登录了,这里跳转到主页面
    if (!Yii::$app->user->isGuest) {
        return $this->goHome();
    }

    // 实例化登录模型 common\models\LoginForm
    $model = new LoginForm();

    // 接收表单数据并调用LoginForm的login方法
    if ($model->load(Yii::$app->request->post()) && $model->login()) {
        return $this->goBack();
    } 

    // 非post直接渲染登录表单
    else {
        return $this->render('login', [
            'model' => $model,
        ]);
    }
}

②、看LoginForm的login等方法的具体实现

use backend\models\UserBackend as User;
/**
 * @inheritdoc
 * 对表单数据进行验证的rule
 */
public function rules()
{
    return [
        // username和password必须
        [['username', 'password'], 'required'],
        // rememberMe是一个boolean值
        ['rememberMe', 'boolean'],
        // 这里需要注意的是 validatePassword 是自定义的验证方法!!!只需要在当前模型内增加对应的认证方法即可
        ['password', 'validatePassword'],
    ];
}

/**
 * 自定义的密码认证方法
 * This method serves as the inline validation for password.
 *
 * @param string $attribute the attribute currently being validated
 * @param array $params the additional name-value pairs given in the rule
 */
public function validatePassword($attribute, $params)
{
    // hasErrors方法,用于获取rule失败的数据
    if (!$this->hasErrors()) {
        // 调用当前模型的getUser方法获取用户
        $user = $this->getUser();

        // 获取到用户信息,然后校验用户的密码对不对,校验密码调用的是 backend\models\UserBackend 的validatePassword方法,
        // 这个我们下面会在UserBackend方法里增加
        if (!$user || !$user->validatePassword($this->password)) {
            // 验证失败,调用addError方法给用户提醒信息
            $this->addError($attribute, 'Incorrect username or password.');
        }
    }
}

/**
 * Logs in a user using the provided username and password.
 *
 * @return boolean whether the user is logged in successfully
 */
public function login()
{
    // 调用validate方法 进行rule的校验,其中包括用户是否存在和密码是否正确的校验
    if ($this->validate()) {
        // 校验成功后,session保存用户信息
        return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
    } else {
        return false;
    }
}

/**
 * 根据用户名获取用户的认证信息
 *
 * @return User|null
 */
protected function getUser()
{
    if ($this->_user === null) {
        // 根据用户名 调用认证类 backend\models\UserBackend 的 findByUsername 获取用户认证信息
        // 这个我们下面会在UserBackend增加一个findByUsername方法对其实现
        $this->_user = User::findByUsername($this->username);
    }

    return $this->_user;
}

③、从上面的②操作中,我们需要在用户认证类 backend\models\UserBackend 中增加 findByUsername方法和validatePassword方法

/**
 * 根据user_backend表的username获取用户
 *
 * @param string $username
 * @return static|null
 */
public static function findByUsername($username)
{
    return static::findOne(['username' => $username]);
}

/**
 * 验证密码的准确性
 *
 * @param string $password password to validate
 * @return boolean if password provided is valid for current user
 */
public function validatePassword($password)
{
    return Yii::$app->security->validatePassword($password, $this->password_hash);
}

最后,我们基本上实现了用户认证类所有的操作,接下来就是见证奇迹的时刻。

7d1d8fbfa8-yii2-user3.png

然后,没报任何错误!!!然后我们就可以直接访问管理平台的首页面site/index了!!!

最后,我们再回过头来看看登录的逻辑实现

7ce606deb2-bloguserlogin.png

至此,我们关于yii2登录注册模块分别讲述完了!哪有大部分同学说的那么难,这不我们也换数据表了,也不难实现的嘛!

下一章节,我们会讲述一下简单的授权访问机制,敬请期待吧!