• Yii2 认证实现原理和示例


    Yii的用户认证分为两个部分,一个是User组件,负责管理用户认证状态的,包括登录,登出,检测当前登录状态等,源文件位于vender/yiisoft/yii2/web/User.php。另一个是实现接口IdentityInterface的模型,同时必须继承ActiveRecord,当用户登录注册时,组件User会通过模型中的接口方法,对用户进行验证。

      对于用户状态切换主要通过switchIdentity方法实现的,比如注册后,用户登录时,会用到User组件中的switchIdentity方法,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    /**
         * Switches to a new identity for the current user.
         *
         * When [[enableSession]] is true, this method may use session and/or cookie to store the user identity information,
         * according to the value of `$duration`. Please refer to [[login()]] for more details.
         *
         * This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]]
         * when the current user needs to be associated with the corresponding identity information.
         *
         * @param IdentityInterface|null $identity the identity information to be associated with the current user.
         * If null, it means switching the current user to be a guest.
         * @param integer $duration number of seconds that the user can remain in logged-in status.
         * This parameter is used only when `$identity` is not null.
         */
        public function switchIdentity($identity$duration = 0)
        {
            $this->setIdentity($identity);
     
            if (!$this->enableSession) {
                return;
            }
     
            /* Ensure any existing identity cookies are removed. */
            if ($this->enableAutoLogin) {
                $this->removeIdentityCookie();
            }
     
            $session = Yii::$app->getSession();
            if (!YII_ENV_TEST) {
                $session->regenerateID(true);
            }
            $session->remove($this->idParam);
            $session->remove($this->authTimeoutParam);
     
            if ($identity) {
                $session->set($this->idParam, $identity->getId());
                if ($this->authTimeout !== null) {
                    $session->set($this->authTimeoutParam, time() + $this->authTimeout);
                }
                if ($this->absoluteAuthTimeout !== null) {
                    $session->set($this->absoluteAuthTimeoutParam, time() + $this->absoluteAuthTimeout);
                }
                if ($duration > 0 && $this->enableAutoLogin) {
                    $this->sendIdentityCookie($identity$duration);
                }
            }
        }

      如果写入identity为null,则将用户状态设置为离线,如果不是null,而是模型实例,该方法会将用户模型的id值写入session中,用户打开新页面时,只需下面的方法就可以判断是否已经登录。

    1
    2
    //已登录返回true
    Yii::$app->user->isGuest

      访问user组件的isGuest属性,会通过魔术方法,调用User组件中的getIsGuest方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
     * Returns a value indicating whether the user is a guest (not authenticated).
     * @return boolean whether the current user is a guest.
     * @see getIdentity()
     */
    public function getIsGuest()
    {
        return $this->getIdentity() === null;
    }

      方法又调用getIdentity()方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    /**
     * Returns the identity object associated with the currently logged-in user.
     * When [[enableSession]] is true, this method may attempt to read the user's authentication data
     * stored in session and reconstruct the corresponding identity object, if it has not done so before.
     * @param boolean $autoRenew whether to automatically renew authentication status if it has not been done so before.
     * This is only useful when [[enableSession]] is true.
     * @return IdentityInterface|null the identity object associated with the currently logged-in user.
     * `null` is returned if the user is not logged in (not authenticated).
     * @see login()
     * @see logout()
     */
    public function getIdentity($autoRenew = true)
    {
        if ($this->_identity === false) {
            if ($this->enableSession && $autoRenew) {
                $this->_identity = null;
                $this->renewAuthStatus();
            else {
                return null;
            }
        }
     
        return $this->_identity;
    }

      当session启用时,通过renewAuthStatus()更新新用户状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    /**
     * Updates the authentication status using the information from session and cookie.
     *
     * This method will try to determine the user identity using the [[idParam]] session variable.
     *
     * If [[authTimeout]] is set, this method will refresh the timer.
     *
     * If the user identity cannot be determined by session, this method will try to [[loginByCookie()|login by cookie]]
     * if [[enableAutoLogin]] is true.
     */
    protected function renewAuthStatus()
    {
        $session = Yii::$app->getSession();
        $id $session->getHasSessionId() || $session->getIsActive() ? $session->get($this->idParam) : null;
     
        if ($id === null) {
            $identity = null;
        else {
            /* @var $class IdentityInterface */
            $class $this->identityClass;
            $identity $class::findIdentity($id);
        }
     
        $this->setIdentity($identity);
     
        if ($identity !== null && ($this->authTimeout !== null || $this->absoluteAuthTimeout !== null)) {
            $expire $this->authTimeout !== null ? $session->get($this->authTimeoutParam) : null;
            $expireAbsolute $this->absoluteAuthTimeout !== null ? $session->get($this->absoluteAuthTimeoutParam) : null;
            if ($expire !== null && $expire < time() || $expireAbsolute !== null && $expireAbsolute < time()) {
                $this->logout(false);
            elseif ($this->authTimeout !== null) {
                $session->set($this->authTimeoutParam, time() + $this->authTimeout);
            }
        }
     
        if ($this->enableAutoLogin) {
            if ($this->getIsGuest()) {
                $this->loginByCookie();
            elseif ($this->autoRenewCookie) {
                $this->renewIdentityCookie();
            }
        }
    }

      该方法主要通过session取出用户id,然后通过id获取用户模型实例,然后使用实例进行登录,和更新认证过期时间。

      下面实现一个常用的用户注册登录功能模块,用户只有登录后才可以进入home页面

      User模型:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    <?php
    /**
     * Created by PhpStorm.
     * User: zhenbao
     * Date: 16/10/17
     * Time: 下午4:14
     */
     
    namespace appmodels;
     
    use Yii;
    use yiiwebIdentityInterface;
    use yiidbActiveRecord;
     
    class User extends ActiveRecord implements IdentityInterface
    {
        const LOGIN = "login";
        const REGISTER = "register";
     
        public function scenarios()
        {
            return [
                self::LOGIN => ['username''password'],
                self::REGISTER => ['username''password']
            ];
        }
     
        public static function tableName()
        {
            return "user";
        }
     
        public static function findIdentity($id)
        {
            return static::findOne($id);
        }
     
        public static function findIdentityByAccessToken($token$type = null)
        {
            return static::findOne(['accessToken' => $token]);
        }
     
        public function getId()
        {
            return $this -> id;
        }
     
        public function getAuthKey()
        {
            return $this -> authKey;
        }
     
        public function validateAuthKey($authKey)
        {
            return $this -> getAuthKey() === $authKey;
        }
     
        public static function findIdentityByUsername($username)
        {
            return static::findOne(['username' => $username]);
        }
     
        public function validatePassword($password)
        {
            return $this -> password === sha1($password);
        }
     
        public function setPassword()
        {
            $this -> password = sha1($this -> password);
            return true;
        }
     
        public function beforeSave($insert)
        {
            if(parent::beforeSave($insert))
            {
                if($this -> isNewRecord)
                {
                    $this -> authKey = Yii::$app -> security -> generateRandomString();
                }
                return true;
            }
            return false;
        }
    }

      控制器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    <?php
     
    namespace appcontrollers;
     
    use Yii;
    use yiiwebController;
    use appmodelsUser;
    use yiiwebCookie;
     
    class UserController extends Controller
    {
        /**默认方法为home方法
         * @var string
         */
        public $defaultAction 'home';
     
        /**用户登录,当已经登录直接跳转home页面,
         * 否则,查看是否有访问accessToken,如果有使用accessToken登录,如果accessToken无效则删除accessToken然后返回到登录界面
         * 如果没有accessToken则使用用户的登录密码登录,验证成功后,查看是否选择了记住我,有则生成accessToken,下次直接使用accessToken登录
         * 登录失败,返回到登录界面重新登录。
         * @return string|yiiwebResponse
         */
        public function actionLogin()
        {
            $request = Yii::$app->request->post('User');
            //如果已经登录获取认证identity然后进入home页面
            if (!(Yii::$app->user->isGuest)) {
                return $this->redirect('/?r=user/home');
            else {
                //如果没有登录,查看cookie中是否有accessToken,如果有尝试使用accessToken登录,accessToken登录失败,则删除这个无效的accessToken
                $accessToken = Yii::$app->request->cookies->getValue('accessToken');
                if ($accessToken !== null) {
                    if (Yii::$app->user->loginByAccessToken($accessToken)) {
                        return $this->redirect("/?r=user/home");
                    else {
                        Yii::$app->request->cookies->remove("accessToken");
                        $user new User(['scenario' => 'login']);
                        return $this->renderPartial('login', ['model' => $user]);
                    }
                }
                //尝试用户名密码登录,如果验证成功,查看是否有点击记住我,如果有生成accessToken,下次直接accessToken登录
                $request = Yii::$app->request->post('User');
                if ($request && isset($request['username']) && isset($request['password'])) {
                    $user = User::findIdentityByUsername($request['username']);
                    if ($user && $user->validatePassword($request['password'])) {
                        $remeberMe = Yii::$app->request->post('remeberMe');
                        if ($remeberMe === 'on') {
                            //生成访问accessToken
                            $user->accessToken = Yii::$app->security->generateRandomString();
                            $user->scenario = 'login';
                            $user->save();
                            Yii::$app->response->cookies->add(new Cookie([
                                'name' => 'accessToken',
                                'value' => $user->accessToken,
                                'expire' => time() + 3600 * 24 * 7
                            ]));
                        }
                        Yii::$app->user->login($user);
                        return $this->redirect('/?r=user/home');
                    }
                }
                //accessToken和账号密码均无法登录,重新返回登录界面
                $user new User(['scenario' => 'login']);
                return $this->renderPartial('login', ['model' => $user]);
     
            }
        }
     
        /**根据用户是否登录,选择跳转页面
         * @return string|yiiwebResponse
         */
        public function actionHome()
        {
            if (Yii::$app->user->isGuest) {
                return $this->redirect('/?r=user/login');
            }
            $user = Yii::$app->user->getIdentity();
            return $this->renderPartial('home', ['user' => $user]);
        }
     
        /**退出登录,如果有删除accessToken,返回登录页面
         * @return yiiwebResponse
         */
        public function actionLogout()
        {
            $user = Yii::$app->user->getIdentity();
            Yii::$app->user->logout($user);
            $accessToken = Yii::$app->request->cookies->getValue('accessToken');
            if ($accessToken !== null) {
                Yii::$app->response->cookies->remove('accessToken');
            }
            return $this->redirect('/?r=user/login');
        }
     
        /**
         * 注册用户,如果注册成功自动进入home主页,注册失败进入注册页面
         * @return string|yiiwebResponse
         */
        public function actionRegister()
        {
            $user new User(['scenario' => 'register']);
            $request = Yii::$app->request->post();
            if ($request) {
                if ($user->load($request) && $user->setPassword() && $user->save()) {
                    Yii::$app->user->login($user);
                    return $this->redirect('/?r=user/login');
                }
            }
            return $this->renderPartial('register', ['model' => $user]);
        }
    }

      视图login

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?php
     
    use yiihelpersHtml;
    use yiiootstrapActiveForm;
     
     $form = ActiveForm::begin([
            'id' => 'user',
            'options' => ['class' => 'form-horizontal'],
            'fieldConfig' => [
                'template' => "{label} <div class="col-lg-3">{input}</div> <div class="col-lg-8">{error}</div>",
                'labelOptions' => ['class' => 'col-lg-1 control-label'],
            ],
        ]); ?>
     
    <?= $form->field($model'username')->textInput(['autofocus' => true]) ?>
    <?= $form->field($model'password')->passwordInput() ?>
    <input type="checkbox" name="remeberMe" >记住我
    <?= Html::submitButton('Login', ['class' => 'btn btn-primary''name' => 'login-button']) ?>
    <?php ActiveForm::end(); ?>
    <a href="/?r=user/register">注册</a>

      视图注册:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php
     
    use yiihelpersHtml;
    use yiiootstrapActiveForm;
     
    $this->title = 'register';
     
    $form = ActiveForm::begin([
        'id' => 'login-form',
        'options' => ['class' => 'form-horizontal'],
        'fieldConfig' => [
            'template' => "{label} <div class="col-lg-3">{input}</div> <div class="col-lg-8">{error}</div>",
            'labelOptions' => ['class' => 'col-lg-1 control-label'],
        ],
    ]); ?>
     
    <?= $form->field($model'username')->textInput(['autofocus' => true]) ?>
    <?= $form->field($model'password')->passwordInput() ?>
    <?= Html::submitButton('Register', ['class' => 'btn btn-primary''name' => 'login-button']) ?>
    <?php ActiveForm::end(); ?>
    <a href="/?r=user/login">登录</a>

      home视图:

    1
    2
    3
    4
    <?php
        echo $user -> username;
    ?>
    <a href="/?r=user/logout">退出</a>

      运行:

    home页面

     

    文档:http://www.yiichina.com/doc/guide/2.0/security-authentication

  • 相关阅读:
    使用SecureCRT连接虚拟机中Linux系统的详细方法以及虚拟网络配置方法
    虚拟机快照克隆多台的方法
    Linux虚拟机网络设置
    Hadoop学习笔记之一:Hadoop IPC
    webpack超详细配置, 使用教程(图文)
    webstrom提示不见了
    vuejs实现本地数据的筛选分页
    关于手机端audio无法自动播放问题解决方法
    计算机实现加法的学习心得
    计算机编码随记
  • 原文地址:https://www.cnblogs.com/qq1069284034/p/8758822.html
Copyright © 2020-2023  润新知