首頁 後端開發 php教程 详细解读PHP的Yii框架中登陆功能的实现_PHP

详细解读PHP的Yii框架中登陆功能的实现_PHP

May 30, 2016 am 08:47 AM
php yii 登陸

Yii的登陆机制

Yii 生成应用时已经提供了最基础的用户登陆机制。我们用 Yii 生成一个新的应用,进入 protected/components 目录,我们可以看到 UserIdentity.php 文件,里面的 UserIdentity 类里面只有一个 public 函数如下:

public function authenticate() 
{ 
  $users=array( 
    // username => password 
    'demo'=>'demo', 
    'admin'=>'admin', 
  ); 
  if(!isset($users[$this->username])) 
    $this->errorCode=self::ERROR_USERNAME_INVALID; 
  elseif($users[$this->username]!==$this->password) 
    $this->errorCode=self::ERROR_PASSWORD_INVALID; 
  else 
    $this->errorCode=self::ERROR_NONE; 
  return !$this->errorCode; 
} 
登入後複製

这个类在 components 里面,会在应用一开始的时候就加载,用于最基础的用户验证,可以看到,该函数一开始只是简单地定义了两个用户 demo 和 admin,而密码也只是 demo 和 admin,如果所以如果你的用户很有限的话,可以直接在这里面修改添加用户就行,多的话我们后面再说。函数下面的 if else 分别是用于检查用户名和密码是否有效,出错的时候生成 ERROR_USERNAME_INVALID,ERROR_PASSWORD_INVALID 这些错误。总的来说,这里进行了真正的用户名密码验证,并进行登陆后的基本逻辑处理。
单看这个类还是看不出登陆控制流程的。遵循 Model/ Control/ View 的原则,我们可以看到登陆流程在这三方面的体现。首先进入 Models 文件夹,你可以看到一个 LoginForm 的类文件,这个类继承了 CFormModel ,为表单模型的派生类,封装了关于登陆的数据及业务逻辑。比较核心的函数如下:

/** 
 * Authenticates the password. 
 * This is the 'authenticate' validator as declared in rules(). 
 */ 
public function authenticate($attribute,$params) 
{ 
  $this->_identity=new UserIdentity($this->username,$this->password); 
  if(!$this->_identity->authenticate()) 
    $this->addError('password','用户名或密码错误'); 
} 
 
/** 
 * Logs in the user using the given username and password in the model. 
 * @return boolean whether login is successful 
 */ 
public function login() 
{ 
  if($this->_identity===null) 
  { 
    $this->_identity=new UserIdentity($this->username,$this->password); 
    $this->_identity->authenticate(); 
  } 
  if($this->_identity->errorCode===UserIdentity::ERROR_NONE) 
  { 
    $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days 
    Yii::app()->user->login($this->_identity,$duration); 
    return true; 
  } 
  else 
    return false; 
} 
登入後複製

这里的 authenticate 利用 UserIdentity 类对用户名密码进行验证,而 login 函数通过检测用户身份是否已经设置及错误码是否为空,最后进行 Yii 提供的 login 函数进行登陆。$duration 可以设置身份的有效期。
再看 Control,在 siteControler 里面有一个 action 是关于登录的,就是 actionLogin, 函数如下:

/** 
 * Displays the login page 
 */ 
public function actionLogin() 
{ 
  if (!defined('CRYPT_BLOWFISH')||!CRYPT_BLOWFISH) 
    throw new CHttpException(500,"This application requires that PHP was compiled with Blowfish support for crypt()."); 
 
  $model=new LoginForm; 
 
  // if it is ajax validation request 
  if(isset($_POST['ajax']) && $_POST['ajax']==='login-form') 
  { 
    echo CActiveForm::validate($model); 
    Yii::app()->end(); 
  } 
 
  // collect user input data 
  if(isset($_POST['LoginForm'])) 
  { 
    $model->attributes=$_POST['LoginForm']; 
    // validate user input and redirect to the previous page if valid 
    if($model->validate() && $model->login()) 
      $this->redirect(Yii::app()->user->returnUrl); 
  } 
  // display the login form 
  $this->render('login',array('model'=>$model)); 
} 
登入後複製

该 login 的 action 是基于 LoginForm 将 POST 的表单进行验证登陆或者渲染一个新的登录页面。

最后, view 的文件是 site 文件夹的 login.php ,这就是你所看到的登陆界面了。

梳理一下,我们可以清楚地看到 Yii 的用户登陆逻辑处理,当你在 login 界面输入用户名密码之后,表单将数据 POST 到 site/login 的动作,loign 实例化了一个 LoginForm 表单模型,并根据 model 里面的 validate 函数 和 login 函数 进行登陆检测,validate 会根据 rule 的规则验证表单数据,其中 password 的验证需要 authenticate 函数,而 authenticate 和 login 函数的验证都是基于 UserIdentity 的 authenticate 函数。所以,如果我们更改登录的逻辑,LgoinForm 和 loginaction 都可以不用修改,直接改 UserIdentity 的 authenticate 函数就基本可以了。

以上的分析是 Yii 自动生成的关于用户登陆的逻辑处理代码,看起来已经很像样了不是吗?但我们的系统一般要支持很多用户访问,在代码里简单地罗列用户名和密码明显是不理智的,更为成熟的当然是请数据库来帮我们管理。假设我们在自己的数据库里面按下面的 Mysql 语句创建一个 admin 的表:

drop table if exists `admin`; 
create table `admin` ( 
  `admin_id` int unsigned not null auto_increment comment '主键', 
  `username` varchar(32) not null comment '登录名', 
  `psw` char(40) not null comment '登录密码(两次sha1)', 
  `nick` varchar(64) not null comment '昵称', 
  `add_time` datetime not null comment '创建时间', 
  `login_time` datetime null comment '最近登录时间', 
  unique key(`username`), 
  primary key (`admin_id`) 
) engine=innodb default charset=utf8 comment='管理员表'; 

登入後複製

Mysql 建表完成后我们就用 gii 生成 admin 的 Model,然后我们可以回到我们最初 Component 里面的 UserIdentity.php 重写 authenticate 函数来实现我们自己的用户名密码验证。为了安全起见,密码采用两次 sha1 加密,所以将采集到的密码两次 sha1 加密,然后在我们创建的 Admin 里面查找是否存在与表单输入的 username 对应的用户,然后比对加密过的密码,如果都通过后就可以把这个用户的常用信息由 setState 函数设置为 Yii 的 user 的用户字段,比如 $this->setState('nick', $user->nick); 这一句之后,以后可以直接通过 Yii:app()->user->nick 来访问当前登陆用户的昵称,而不用去查询数据库。而 $user->login_time = date('Y-m-d H:i:s'); 是进行更新用户登陆时间,并通过下一句的 save 保存到数据库中。

public function authenticate() 
{ 
  if(strlen($this->password) > 0) 
    $this->password = sha1(sha1($this->password)); 
  $user = Admin::model()->findByAttributes(array('username' => $this->username)); 
  if($user == null) 
    $this->errorCode=self::ERROR_USERNAME_INVALID; 
  elseif( !($user instanceof Admin) || ($user->psw != $this->password) ) 
    $this->errorCode=self::ERROR_PASSWORD_INVALID; 
  else 
  { 
    $this->setState('admin_id', $user->admin_id); 
    $this->setState('nick', $user->nick); 
    $this->setState('username', $user->username); 
    $user->login_time = date('Y-m-d H:i:s'); 
    $user->save(); 
    $this->errorCode=self::ERROR_NONE; 
  } 
  return !$this->errorCode; 
} 

登入後複製

而如果你想要修改登陆的界面,那就进入 view 里面 site 文件夹中的 login.php ,尽情地折腾让它变成你想要的样子,这样我们自己的登陆流程也完成了。有了 Yii 是不是方便极了~


设置自动登陆
自动登录的原理很简单。主要就是利用cookie来实现的
在第一次登录的时候,如果登录成功并且选中了下次自动登录,那么就会把用户的认证信息保存到cookie中,cookie的有效期为1年或者几个月。

在下次登录的时候先判断cookie中是否存储了用户的信息,如果有则用cookie中存储的用户信息来登录,

配置User组件

首先在配置文件的components中设置user组件

    'user' => [
      'identityClass' => 'app\models\User',
      'enableAutoLogin' => true,
    ],

登入後複製

我们看到enableAutoLogin就是用来判断是否要启用自动登录功能,这个和界面上的下次自动登录无关。
只有在enableAutoLogin为true的情况下,如果选择了下次自动登录,那么就会把用户信息存储起来放到cookie中并设置cookie的有效期为3600*24*30秒,以用于下次登录

现在我们来看看Yii中是怎样实现的。

一、第一次登录存cookie

1、login 登录功能

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

    return !$this->getIsGuest();
  }

登入後複製

在这里,就是简单的登录,然后执行switchIdentity方法,设置认证信息。

2、switchIdentity设置认证信息

  public function switchIdentity($identity, $duration = 0)
  {
    $session = Yii::$app->getSession();
    if (!YII_ENV_TEST) {
      $session->regenerateID(true);
    }
    $this->setIdentity($identity);
    $session->remove($this->idParam);
    $session->remove($this->authTimeoutParam);
    if ($identity instanceof IdentityInterface) {
      $session->set($this->idParam, $identity->getId());
      if ($this->authTimeout !== null) {
        $session->set($this->authTimeoutParam, time() + $this->authTimeout);
      }
      if ($duration > 0 && $this->enableAutoLogin) {
        $this->sendIdentityCookie($identity, $duration);
      }
    } elseif ($this->enableAutoLogin) {
      Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie));
    }
  }

登入後複製

这个方法比较重要,在退出的时候也需要调用这个方法。

这个方法主要有三个功能
设置session的有效期
如果cookie的有效期大于0并且允许自动登录,那么就把用户的认证信息保存到cookie中
如果允许自动登录,删除cookie信息。这个是用于退出的时候调用的。退出的时候传递进来的$identity为null

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

存储在cookie中的用户信息包含有三个值:

  1. $identity->getId()
  2. $identity->getAuthKey()
  3. $duration

getId()和getAuthKey()是在IdentityInterface接口中的。我们也知道在设置User组件的时候,这个User Model是必须要实现IdentityInterface接口的。所以,可以在User Model中得到前两个值,第三值就是cookie的有效期。

二、自动从cookie登录

从上面我们知道用户的认证信息已经存储到cookie中了,那么下次的时候直接从cookie里面取信息然后设置就可以了。

1、AccessControl用户访问控制

Yii提供了AccessControl来判断用户是否登录,有了这个就不需要在每一个action里面再判断了

 public function behaviors()
  {
    return [
      'access' => [
        'class' => AccessControl::className(),
        'only' => ['logout'],
        'rules' => [
          [
            'actions' => ['logout'],
            'allow' => true,
            'roles' => ['@'],
          ],
        ],
      ],
    ];
  }
登入後複製

2、getIsGuest、getIdentity判断是否认证用户

isGuest是自动登录过程中最重要的属性。
在上面的AccessControl访问控制里面通过IsGuest属性来判断是否是认证用户,然后在getIsGuest方法里面是调用getIdentity来获取用户信息,如果不为空就说明是认证用户,否则就是游客(未登录)。

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

    return $this->_identity;
  }

登入後複製

3、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 IdentityInterface $class */
      $class = $this->identityClass;
      $identity = $class::findIdentity($id);
    }

    $this->setIdentity($identity);

    if ($this->authTimeout !== null && $identity !== null) {
      $expire = $session->get($this->authTimeoutParam);
      if ($expire !== null && $expire < time()) {
        $this->logout(false);
      } else {
        $session->set($this->authTimeoutParam, time() + $this->authTimeout);
      }
    }

    if ($this->enableAutoLogin) {
      if ($this->getIsGuest()) {
        $this->loginByCookie();
      } elseif ($this->autoRenewCookie) {
        $this->renewIdentityCookie();
      }
    }
  }

登入後複製

这一部分先通过session来判断用户,因为用户登录后就已经存在于session中了。然后再判断如果是自动登录,那么就通过cookie信息来登录。

4、通过保存的Cookie信息来登录 loginByCookie

 protected function loginByCookie()
  {
    $name = $this->identityCookie['name'];
    $value = Yii::$app->getRequest()->getCookies()->getValue($name);
    if ($value !== null) {
      $data = json_decode($value, true);
      if (count($data) === 3 && isset($data[0], $data[1], $data[2])) {
        list ($id, $authKey, $duration) = $data;
        /** @var IdentityInterface $class */
        $class = $this->identityClass;
        $identity = $class::findIdentity($id);
        if ($identity !== null && $identity->validateAuthKey($authKey)) {
          if ($this->beforeLogin($identity, true, $duration)) {
            $this->switchIdentity($identity, $this->autoRenewCookie &#63; $duration : 0);
            $ip = Yii::$app->getRequest()->getUserIP();
            Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);
            $this->afterLogin($identity, true, $duration);
          }
        } elseif ($identity !== null) {
          Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
        }
      }
    }
  }
登入後複製

先读取cookie值,然后$data = json_decode($value, true);反序列化为数组。

这个从上面的代码可以知道要想实现自动登录,这三个值都必须有值。另外,在User Model中还必须要实现findIdentity、validateAuthKey这两个方法。

登录完成后,还可以再重新设置cookie的有效期,这样便能一起有效下去了。

 $this->switchIdentity($identity, $this->autoRenewCookie &#63; $duration : 0);

登入後複製

三、退出 logout

 public function logout($destroySession = true)
  {
    $identity = $this->getIdentity();
    if ($identity !== null && $this->beforeLogout($identity)) {
      $this->switchIdentity(null);
      $id = $identity->getId();
      $ip = Yii::$app->getRequest()->getUserIP();
      Yii::info("User '$id' logged out from $ip.", __METHOD__);
      if ($destroySession) {
        Yii::$app->getSession()->destroy();
      }
      $this->afterLogout($identity);
    }

    return $this->getIsGuest();
  }


  public function switchIdentity($identity, $duration = 0)
  {
    $session = Yii::$app->getSession();
    if (!YII_ENV_TEST) {
      $session->regenerateID(true);
    }
    $this->setIdentity($identity);
    $session->remove($this->idParam);
    $session->remove($this->authTimeoutParam);
    if ($identity instanceof IdentityInterface) {
      $session->set($this->idParam, $identity->getId());
      if ($this->authTimeout !== null) {
        $session->set($this->authTimeoutParam, time() + $this->authTimeout);
      }
      if ($duration > 0 && $this->enableAutoLogin) {
        $this->sendIdentityCookie($identity, $duration);
      }
    } elseif ($this->enableAutoLogin) {
      Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie));
    }
  }

登入後複製

退出的时候先把当前的认证设置为null,然后再判断如果是自动登录功能则再删除相关的cookie信息。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 Dec 24, 2024 pm 04:42 PM

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 Dec 20, 2024 am 11:31 AM

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

我後悔之前不知道的 7 個 PHP 函數 我後悔之前不知道的 7 個 PHP 函數 Nov 13, 2024 am 09:42 AM

如果您是經驗豐富的PHP 開發人員,您可能會感覺您已經在那裡並且已經完成了。操作

您如何在PHP中解析和處理HTML/XML? 您如何在PHP中解析和處理HTML/XML? Feb 07, 2025 am 11:57 AM

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

在PHP API中說明JSON Web令牌(JWT)及其用例。 在PHP API中說明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

php程序在字符串中計數元音 php程序在字符串中計數元音 Feb 07, 2025 pm 12:12 PM

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

解釋PHP中的晚期靜態綁定(靜態::)。 解釋PHP中的晚期靜態綁定(靜態::)。 Apr 03, 2025 am 12:04 AM

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? 什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? Apr 03, 2025 am 12:03 AM

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

See all articles