1. 程式人生 > >Yii2.0 探究三 :使用者登入機制

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我們逐步去分析;

<?php
/**
 * @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去實現方法:

<?php
/**
 * @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>
            <!-- /.col -->
            <div class="col-xs-4">
                <?= Html::submitButton('登入', ['class' => 'btn btn-primary btn-block btn-flat', 'name' => 'login-button']) ?>
            </div>
            <!-- /.col -->
        </div>


        <?php ActiveForm::end(); ?>
        <!-- /.social-auth-links -->

        <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