1. 程式人生 > >Yii2 User 登入原理

Yii2 User 登入原理

本文是在session登入暢通無阻的前提下,首先要先搞好session登入,這個在這裡不做敘述,好多session登入的文章。本文敘述cookie登入的原理和使用

1.具體實現:

1.1 cookie登入配置config方面,配置好的程式碼如下:

    'components' => [
        'user' => [
           'identityClass' => 'fecshop\models\mysqldb\Customer',
           'enableAutoLogin' => true,
           'authTimeout'
=> 3600, ], ],

enableAutoLogin設定為true,就會使用cookie登入,如果設定了enableAutoLogin,那麼下面的超時時間authTimeout就會無效,因為這個引數是session的超時時間,不過,我們可以在登入的時候,超時時間也從這個配置引數讀取,譬如下面的函式引數$duration,可以從這裡讀取,如果這樣,就會有效。

    $duration = \Yii::$app->user->authtimeout;
    \Yii::$app->user->login($this->getCustomer(), $duration
);

上面的結論通過程式碼解釋,如下:

    public function login(IdentityInterface $identity, $duration = 0)
        {
            if ($this->beforeLogin($identity, false, $duration)) {
                $this->switchIdentity($identity, $duration);
                $id = $identity->getId();
                $ip = Yii::$app->getRequest()->getUserIP();
                if
($this->enableSession) { $log = "User '$id' logged in from $ip with duration $duration."; } else { $log = "User '$id' logged in from $ip. Session not enabled."; } Yii::info($log, __METHOD__); $this->afterLogin($identity, false, $duration); } return !$this->getIsGuest(); }

檢視 $this->switchIdentity($identity, $duration);

    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);
                }
            }
        }

檢視:$this->sendIdentityCookie($identity, $duration);

    protected function sendIdentityCookie($identity, $duration)
        {
            $cookie = new Cookie($this->identityCookie);
            $cookie->value = json_encode([
                $identity->getId(),
                $identity->getAuthKey(),
                $duration,
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
            $cookie->expire = time() + $duration;
            Yii::$app->getResponse()->getCookies()->add($cookie);
        }

通過一層層的函式可以看到,cookie的超時時間是從login()函式中讀取出來的,

而不是從\yii\web\User的authTimeout變數讀取。最後傳遞給sendIdentityCookie($identity, $duration)方法,進而設定cookie的超時時間。

另外配置中開啟了enableAutoLogin,但是在呼叫login方法的時候沒有設定超時時間變數$duration,同樣不會設定cookie,進而配置 enableAutoLogin 無效,下面是程式碼解釋:

public function switchIdentity($identity, $duration = 0){ 

 #函式中的程式碼

    if ($duration > 0 && $this->enableAutoLogin) {
        $this->sendIdentityCookie($identity, $duration);
    }
}

可以看到,如果超時時間為0,那麼不會執行設定cookie的方法

public function sendIdentityCookie($identity, $duration); 進而不會設定cookie

1.2 程式碼改進: 參考程式碼:

    public function getCustomer(){
          if($this->_customer === null){
            $this->_customer = Customer::findByEmail($this->email);
          }
          return $this->_customer;
          
        }
        
        public function login($duration = 86400)
        {
          if ($this->validate()) {
            //return \Yii::$app->user->login($this->getAdminUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
            return \Yii::$app->user->login($this->getCustomer(), $duration);
          } else {
            return false;
          }
        }

在上面的程式碼,預設cookie的過期時間為86400秒,這樣預設就不會cookie超時 如果我我呼叫:

    $model = new CustomerLogin;
    $model->email = $data['email'];
    $model->password = $data['password'];
    $loginStatus = $model->login(0);

由於過期時間填寫為0,因此,即使在user元件中開啟配置enableAutoLogin=true, cookie也不會生效。

另外,我如果從\yii\web\User的authTimeout變數讀取。來設定cookie的超時時間,也是一個不錯的選擇,程式碼如下:

    public function login($duration = 0)
        {
        if(!$duration){
          if(Yii::$app->user->authTimeout){
            $duration = Yii::$app->user->authTimeout;
          }
        }
        if ($this->validate()) {
                //return \Yii::$app->user->login($this->getAdminUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
          return \Yii::$app->user->login($this->getCustomer(), $duration);
            } else {
                return false;
            }
        }
  1. cookie超時時間重新整理。

在預設的情況下,如果登入成功賬戶,每次請求訪問Yii::$app->user->identity,都會重新整理cookie的超時時間,設定自動更新的變數為: public $autoRenewCookie = true;

該變數預設為true,所以不需要在配置中設定這個變數,使用預設就好。

cookie超時時間重新整理的原理解釋,下面是詳細程式碼:

執行Yii::$app->user->identity,對應的是下面的函式

    public function getIdentity($autoRenew = true)
       {
           if ($this->_identity === false) {
               if ($this->enableSession && $autoRenew) {
                   $this->_identity = null;
                   $this->renewAuthStatus();
               } else {
                   return null;
               }
           }
           return $this->_identity;
       }

會執行renewAuthStatus()方法

    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();
               }
           }
       }

如果enableAutoLogin開啟,如果登入就會執行renewIdentityCookie方法。

    protected function renewIdentityCookie()
       {
           $name = $this->identityCookie['name'];
           $value = Yii::$app->getRequest()->getCookies()->getValue($name);
           if ($value !== null) {
               $data = json_decode($value, true);
               if (is_array($data) && isset($data[2])) {
                   $cookie = new Cookie($this->identityCookie);
                   $cookie->value = $value;
                   $cookie->expire = time() + (int) $data[2];
                   Yii::$app->getResponse()->getCookies()->add($cookie);
               }
           }
       }

該方法會重新設定超時時間,通過上面的幾個函式知道了原理,那麼問題來了,如果我登入了使用者,超時時間設定的為10秒,我開始瀏覽器文章或者幹其他的,每次訪問間隔3秒,如果這些頁面都沒有執下面的行程式碼:Yii::$app->user->identity

那麼,10秒後,使用者登入狀態就會被超時,需要重新登入,(有一些間接的方法也會執行上面的程式碼,譬如Yii::$app->user->isGuest ,就會間接呼叫執行Yii::$app->user->identity,這種情況下不會超時)。

    public function getIsGuest()
       {
           return $this->getIdentity() === null;
       }

因此,如果登入狀態要持久下去,那麼為了保持登入狀態,每個頁面請求後,都需要執行Yii::$app->user->identity,然後cookie的超時時間就會更新,這樣3秒請求一次,登入狀態會一直保持下去。

上面是yii2 cookie使用過程中要注意的一些問題。總體來說還是不錯,但在實際過程中,還需要結合一下其他,譬如cart 和customer login都使用cookie,超時時間要一致,那麼,cart和customer的cookie超時時間要一致,並且,在每次呼叫Yii::$app->user->identity,都必須執行更新cart的cookie超時時間,因此,需要重寫Yii\web\User。