Yii2.0 探究三 :用户登录机制

前言:做后台管理的首要任务当然是登陆、注册;就yii来说,它为我们封装好了用户的验证方法,验证过程,所以,我们要做的就是模仿,既然接触了这个框架,就要照这个框架来思考,也就是Tink in YII,用自身的验证而不自己验证的方法有以下几点好处:

一、为什么推荐你用自带的登陆?

  1. yii2.0框架为我们封装好了用户的信息;
    比如:
    \Yii::$app->user->isGust 判断是否为访客
    Yii::$app->user->identity 这是一个登陆用户的信息;里面包括了当前登录用户的全部信息(user数据表中所有的字段);登录后可以自行 var_dump去打印查看;
    比如

    获取当前用户名: Yii::$app->user->identity->username
    获取当前用户的ip: Yii::$app->user->identity->login_ip

    所以,我们不用费劲的去编写象下面的这样一个数组去判读
    [IsLogin=>1,Username=>’李华’]

  2. 自带csrf,和密码加密技术bcrypt加密方式,php自带的crypt函数去加密成的hash值;bcrypt 哈希是公认加密程度较好的加密方式(相对于md5和sha1来说,有破解md5的彩虹表不是说这种加密方式不安全,可自行google)。

  3. 就实现来说:这只是基本,登录搞不定,可以说用yii开发就寸步难行,大神除外,自己定义框架实现的除外。这是一个流程,登录后很多控制器方法都要求去用”是不是访客”去判断;

所以我们还是乖乖用这种方式,去习惯、理解Yii框架的运作方式;闲话休说,我带大家一步一步的分析源码和流程;

二、我们讲后台如何通过邮箱和用户名完美登录?

步骤一:前后端登录分离;

  • . 默认生成的前端用户表的model在common>models文件夹下面

User.php;我们要做的就是copy一份;重命名为AdminUser.php,并且把类名改为AdminUser作为我们的后台用户表的model部分

  • . 分别去到frontend和backend下面的config>main.php中配置登录

    的session和cookie

'components' => [
        'request' => [
            'csrfParam' => '_csrf-backend',
        ],
        'user' => [
        //重点是这,配置验证用户的类是AdminUser;也就是后台用户表
            'identityClass' => 'common\models\AdminUser',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
        ],
        'session' => [

            'name' => 'advanced-backend',
        ],

这一步是为了彻底区分前后台,前台有前台的cookie和session;后台有后台的cookie和session前台把backend改为frontend就好,
千万别少了session这个配置

步骤二:后台管理员表adminuser的建立,(我们这次采取命令行的方式,也可以自己创建sql);

1. 自己建表的注意 console>migrations这个文件夹有用户表的结构,照着这个字段去建,并且上面的一个字段都别少,都有用;

2.用命令行比较方便,我介绍下:(刚接触框架的建议跳过

位置:console>migrations下面就是表的数据文件;下面我们就用命令

行来建表,win下面调用win+R进入cd项目目录


D:\wamp\wamp64\www\site\angular>

输入:yii migrate/create adminuser
Yii Migration Tool (based on Yii v2.0.10)

Create new migration 'D:\wamp\wamp64\www\site\angular\console/migrations\m161127_162856_ad
minuser.php'? (yes|no) [no]:
输入:yes
New migration created successfully.

命令行中console中进入到项目输入

yii migrate/create adminuser这个命令就是新建adminuser表

的控制台模型(不起作用了在前面加php)

这时候会在console>migrations下出现一个文件类似

这样m161127_162856_adminuser.php的文件,

我们可以在 这个文件中新建我们的表模型;

我们copy上一个存在的user表模型;

class m161127_162856_adminuser extends Migration(这里千万别改,只copy里面的方法就行)
{
    public function up()
    {
        $tableOptions = null;
        if ($this->db->driverName === 'mysql') {

            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
        }

        $this->createTable('{{%adminuser}}', [
            'id' => $this->primaryKey(),
            'username' => $this->string()->notNull()->unique(),
            'auth_key' => $this->string(32)->notNull(),
            'password_hash' => $this->string()->notNull(),
            'password_reset_token' => $this->string()->unique(),
            'email' => $this->string()->notNull()->unique(),
            'status' => $this->smallInteger()->notNull()->defaultValue(10),
            'created_at' => $this->integer()->notNull(),
            'updated_at' => $this->integer()->notNull(),
        ], $tableOptions);
    }

    public function down()
    {
        $this->dropTable('{{%adminuser}}');
    }

    /*
    // Use safeUp/safeDown to run migration code within a transaction
    public function safeUp()
    {
    }

    public function safeDown()
    {
    }
    */
}

这样表就建模完成,剩下的就是命令行执行建表命令了。

D:\wamp\wamp64\www\site\angular>
输入:yii migrate
Yii Migration Tool (based on Yii v2.0.10)

Creating migration history table "migration"...Done.
Total 2 new migrations to be applied:
        m130524_201442_init
        m161127_162856_adminuser

Apply the above migrations? (yes|no) [no]:yes
*** applying m130524_201442_init
    > create table {{%user}} ... done (time: 1.555s)
*** applied m130524_201442_init (time: 1.850s)

*** applying m161127_162856_adminuser
    > create table {{%adminuser}} ... done (time: 0.404s)
*** applied m161127_162856_adminuser (time: 0.436s)


2 migrations were applied.

Migrated up successfully.

我们去数据库看下,果然大功告成,两个表user、adminuser已经新建完

成;这一步我们学会了很多yii的控制台命令:

yii+....
   migrate                         数据库中添加model
   migrate/create admin            创建表
   migrate/down                    回滚表(删除已经创建的)              
   migrate/up (default)            更新表(更新新建的表模型)

当然也可以新建类似这样结构的表;

步骤三:登录开始

1. //登录方法


 public function actionLogin()
    {
        if (!Yii::$app->user->isGuest) {
            return $this->goHome();
        }
//注意是backend/models下的loginForm,自己去仿照common下的loginForm去建,这个我下一步说
        $model = new backend\models\LoginForm();

       //通过post给model数据,并用login方法去判断是否能登录        
        if ($model->load(Yii::$app->request->post()) && $model->login()) {

            return $this->goBack();

        } else {
            return $this->render('login', [
                'model' => $model,
            ]);
        }
    }

2.backend\models\LoginForm的创建,也就是登录表的表单模型,区别于数据库的model

表单模型是不一定用到数据库的,我们在backend->models下面建的这个LoginForm就是上一步我们处理登录表单的模型;

下面是我写的一个通过邮箱和用户名登录的Form我们逐步去分析;


/**
 * @see https://github.com/craftsmann.
 */
namespace backend\models;
use Yii;
use common\models\AdminUser;
use yii\db\ActiveRecord;

class LoginForm extends ActiveRecord{

    public $user;
    protected $_user;
    public $pass;
    public $verify;
    public $remember =true;

    public function rules()
    {
        return [
            [['user','pass'],'required'],
            ['user','string','length'=>[5,20]],
            ['pass','string','min'=>5],
            //添加自定义验证密码的函数verifyPass
            ['pass','verifyPass'],
            ['verify','captcha']
        ];

    }
    public function verifyPass($attr){

        if(!$this->hasErrors()){
        //其实就是获取user对象集合
            $person = $this->getUser();

        //获取用户失败或者验证密码失败;validatePassword是需要我们在AdminUser中去实现的;

            if(!$person || !$person->validatePassword($this->pass)){

                $this->addError($attr,'用户或密码错误');

            }
        }
    }

    //这个方法很关键,我们通过user去接收的字符串可能是用户名或者邮箱,我们分情况作出返回
    protected function getUser(){

            //判断是user字段,是否为邮箱格式;
            if(preg_match('/^[a-zA-Z0-9-_]+(\.\w+)*@[a-zA-Z0-9-_]+\.(\w+)$/',$this->user)){

        //这个findByEmail是我们要去AdminUser这个数据库model中去实现的;其实原理就是根据email查找数据,有了就能获取对象
               return AdminUser::findByEmail($this->user);

            //判断是否为用户名,
            }else if(preg_match('/^\w+$/',$this->user)){
                //这个findByUsername是我们要去AdminUser这个数据库model中去实现的,上面理解一样
                return AdminUser::findByUsername($this->user);
            }else{
                throw new Exception('登录失败');
            }
        }else{
            return $this->_user;
        }
    }

    public function login(){
        //这个登录方法是yii框架封装的注意;Yii::$app->user->login(用户对象,时间);其实就是保存到session中,以便登录的用户可取得数据
        return !$this->validate()?false:Yii::$app->user->login($this->getUser(),$this->remember?60*60*1:0);
    }

    public function attributeLabels()
    {
        return [
            'user'=>'用户名/邮箱',
            'pass'=>'密码',
            'verify'=>'验证码'
        ];
    }

}

3.去AdminUser去实现方法:


/**
 * @see https://github.com/craftsmann.
 */
namespace common\models;
use Yii;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
use yii\behaviors\TimestampBehavior;

//IdentityInterface去实现了这个用户验证的接口,其实做登录的一般用不到里面的方法,做注册就会用到,实际是一种验证的规范,没卵用的,学过面向对象就会知道一旦去实现这个接口,里面的方法都要去实现的,可以找到这个类,里面注释中有方法,copy一份粘到我们的AdminUser中就行了;

class AdminUser extends ActiveRecord implements IdentityInterface{

    //定义用户登陆状态
    const USER_ACTIVE   = 10;
    const USER_DISABLED =  0;

    //返回adminuser表
    public static function tableName()
    {
        return '{{%adminuser}}';
    }

    //时间自动更新updated和created
    public function behaviors()
    {
        return [
            TimestampBehavior::className()
        ];
    }

    /**
     * 说白了就是通过用户名找到adminuser数据表集合,并返回出去
     * @param $user
     * @return static
     */
    public static function findByUsername($user){

        return static::findOne(['username'=>$user,'status'=>self::USER_ACTIVE]);

    }

    /**
     * 说白了就是通过email找到adminuser数据表集合,并返回出去
     * @param $user
     * @return static
     */
    public static function findByEmail($user){
        return static::findOne(['email'=>$user,'status'=>self::USER_ACTIVE]);
    }

    /**
     * 检验密码,这个也是yii特有的加密验证规则
     * @param $pass
     * @return bool
     */
    public function validatePassword($pass){

        return Yii::$app->security->validatePassword($pass,$this->password_hash);
    }

//下面这一坨就是我们要从这个接口中要去实现的方法
    public static function findIdentity($id)
      {
         return static::findOne($id);
     }

     public static function findIdentityByAccessToken($token, $type = null)
      {
            return static::findOne(['access_token' => $token]);
         }

     public function getId()
     {
           return $this->id;
         }

     public function getAuthKey()
     {
            return $this->auth_key;
     }

     public function validateAuthKey($authKey)
     {
         return $this->auth_key === $authKey;
     }
}

3.去render一个login的表单:去检验我们的方式,基本就大功告成了

<div class="login-box-body">
        <p class="login-box-msg">后台登录p>

        php $form = ActiveForm::begin(['id' => 'login-form', 'enableClientValidation' => false]); ?>

        form
            ->field($model, 'user', $fieldOptions1)
            ->label(false)
            ->textInput(['placeholder' => $model->getAttributeLabel('用户名')]) ?>

        form
            ->field($model, 'pass', $fieldOptions2)
            ->label(false)
            ->passwordInput(['placeholder' => $model->getAttributeLabel('密码')]) ?>

        <div class="row">
            <div class="col-xs-8">
                form->field($model, 'remember')->checkbox() ?>
            div>
            
            <div class="col-xs-4">
                Html::submitButton('登录', ['class' => 'btn btn-primary btn-block btn-flat', 'name' => 'login-button']) ?>
            div>
            
        div>


        php ActiveForm::end(); ?>
        

        <a href="#">忘记密码?a><br>

    div>

三、常见问题?

Q:我的登陆成功后不跳转,我的登陆成功后跳转别的页面?:

A:配置文件中session和cookie缺少配置,详情见上面;

Q:我的自动登录失败?:

A:user组件的自动登录缺少;

'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true],
        ],

Q:我的登陆不成功?:

A:……..;

四、有不懂的可以留言,我尽量解决,相互交流(搞基),互相促进(搞基)。

The End

你可能感兴趣的:(yii2.0)