1. 用戶認證體系基本概念及實現;
- 相關概念:
- 認證: 鑑定用戶身份的過程。它通常使用一個標識符(如用戶名或電子郵件地址)和一個加密令牌(比如密碼或者存取令牌)來鑑別用戶身份
- 認證是登錄功能的基礎
- 認證框架:
- 相關組件:該框架連接了不用的組件以支持登錄。欲使用這個框架,主要需要做一下工作
- 設置用戶組件 yii\web\User;
- 要想使用組件,需要創建一個類實現 yii\web\IdentityInterface 接口,再連接 yii\web\User 組件,就可以實現登錄、退出過程的操作
- 用戶組件
yii\web\User
:
- 用來管理用戶的認證狀態(登錄、退出、獲取用戶信息)
- 需要制定一個含有實際認證邏輯的認證類繼承實現 yii\web\IdentityInterface 接口,實現以後再去連接 yii\web\User 組件,去完成認證服務
- 爲什麼有了 yii\web\User 組件,爲什麼還要去實現一個邏輯,再去連接組件?yii\web\User 組件只是提供了一個基本的框架,但是具體的實現邏輯需要按照具體的業務邏輯去實現,每個 Web 應用的認證邏輯都是不一樣的。所以需要去實現延展 yii\web\IdentityInterface 這個接口(規範)。實現之後再連接組件,才能提供服務。
- 實現後的實例作爲 yii\web\User 組件的身份驗證實例。
- 認證接口
yii\web\IdentityInterface
需要實現的方法
- yii\web\IdentityInterface::
findIdentity()
:根據用戶的 id 查找認證模型類的實例,當使用 session 來維持登錄狀態的時候,會用到這個方法
- yii\web\IdentityInterface::
findIdentityByAccessToken()
:根據 AccessToken 的值來獲取用戶認證實例。通常 AccessToken 和用戶是進行邦定的,這種一般運用在無狀態的 RESTful 這樣的應用下
- yii\web\IdentityInterface::
getId()
:獲取用戶認證實例 id
- yii\web\IdentityInterface::
getAuthKey()
:獲取基於 cookie 登錄時的一個認證祕鑰
- yii\web\IdentityInterface::
ValidateAuthKey()
:認證 cookie 登錄祕鑰的內容
- 不一定所有的方法都要實現,不實現的方法可以留空
- 實例
- 實例頁面:http://192.168.2.214/yii22/basic/web/index.php?r=site/login
$config = [
'components' => [
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
],
],
];
class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface{}
public function actionLogin() {
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
}
$model->password = '';
return $this->render('login', [
'model' => $model,
]);
}
public function login(){
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
}
return false;
}
- 總結用戶登錄流程
- 首先頁面點擊登錄的時候,提交給了 basic/controllers/SiteController.php 的 actionLogin()
- 第一步先 new LoginForm() 實例化,調用 basic/models/LoginForm.php 裏的 login() 方法,這個方法會去調用組件的 Yii::$app->user->login() 方法,把用戶實例和相關內容傳遞進去,就會自動的往 cookie 和 session 裏寫入用戶相關信息,保存了登錄狀態
- basic/models/User.php 作用就是繼承 \yii\web\IdentityInterface 接口,實現方法
- private static $users 指定了兩個用戶,
- findIdentity($id) 方法獲取用戶實例,user 組件會自動調用這個方法
- findIdentityByAccessToken($token, $type = null) 根據 Token 獲取用戶實例
- findByUsername( $username ) 是自己寫的方法,根據用戶名將用戶實例返回。在 LoginForm() 裏面的 getUser() 方法,用到了 findByUsername( $username ) 方法獲取用戶實例,在 LoginForm() 裏面的 login() 方法中,傳遞給 user 組件裏的 login() 方法
- getId() 獲取 model 實例 id
- 退出流程
- 頁面右上角點擊退出的時候,會調用 basic/controllers/SiteController.php 中的 actionLogout()
<?php
public function actionLogout(){
Yii::$app->user->logout();
return $this->goHome();
}
2. 用戶認證組件 User 相關屬性和方法完成前臺的登錄和退出;
- 用戶組件
yii\web\User
相關屬性:
- identity:Yii::$app->user->
identity
;當前用戶的身份實例,未認證用戶則爲 NULL
- id:Yii::$app->user->
id
;當前用戶的 id,未認證用戶則爲 NULL
- isGuest:Yii::$app->user->
isGuest
;判斷當前用戶是否爲遊客(未認證)
- 相關方法:
- login:將當前用戶的身份登記到 yii\web\User(第一個參數傳遞用戶實例,第二個參數爲保留的登錄時間)
- ‐ 登錄相關屬性 1:enableSession,是否啓用 session 來保存會話狀態,默認 true
- ‐ 相關屬性 2:enableAutoLogin,是否啓用自動登錄,默認 true,可以在配置文件中配置
- logout:註銷用戶,啓用 session 時註銷用戶纔有意義。可以傳遞布爾值,傳遞 true 會把 session 信息全部清除,傳遞 false 會保留會話數據
<?php
public function actionAuth(){
$model = new User;
if(Yii->$app->request->isPost){
$post = Yii->$app->request->Post();
if($model->login($post)){
%url = Yii::$app->session->getFlash('referrer');
return $this;
}
}
return $this->render("auth", ['model' => $model])
}
public function actionLogout(){
Yii->$app->session->remove('loginname');
Yii->$app->session->remove('isLogin');
if(!isset(Yii->$app->session['isLogin'])){
return $this->goBack(Yii:$app->request->referer);
}
}
public function login($data){
$this->scenario = "login";
if($this->load($data) && $this->validate()){
$lifetime = $this->rememberMe ? 24*3600 : 0;
$session = Yii::$app->session;
session_set_cookie_params($lifetime);
$session['loginname'] = $this->loginname;
$session['isLogin'] = 1;
return (bool)$session['isLogin'];
}
return false;
}
- 通過用戶組件去完成登錄操作:
- 在 basic/config/web.php 中設置(components),然後繼承 \yii\web\IdentityInterface 接口,實現 5 個方法
<?php
namespace app\models;
use yii\db\ActiveRecord;
use Yii;
class User extends ActiveRecord implements \yii\web\IdentityInterface{
public static function findIdentity($id){
return static::findOne($id);
}
public static function findIdentityByAccessToken($token, $type = null){
return NULL;
}
public function getId(){
return $this->id;
}
public function getAuthKey(){
return '';
}
public function validateAuthKey($authKey){
return true;
}
public function getUser(){
return self::find()->where('username = :loginname or useremail = :loginname', [
':loginname' => $this->loginname
])->one();
}
public function login($data){
$this->scenario = "login";
if($this->load($data) && $this->validate()){
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 24*3600 : 0);
}
return false;
}
}
- 登錄成功後頁面顯示用戶名:
- 不用 session,使用 user 組件返回的一些內容進行操作
<?php
use yii\helpers\Html;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
?>
<?php
NavBar::begin([
'options' =>[
'class' => 'top-bar animate-dropdown',
],
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-left'],
'item' => [
['label' => '首頁', 'url' => ['/site/index']],
!\Yii::$app->user->isGuest ? (
['label' => '購物車', 'url' => ['/cart/index']]
) : '',
!\Yii::$app->user->isGuest ? (
['label' => '我的訂單', 'url' => ['/cart/index']]
) : '',
],
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-right'],
'item' => [
\Yii::$app->user->isGuest ? (
['label' => '註冊', 'url' => ['/member/auth']]
) : '',
\Yii::$app->user->isGuest ? (
['label' => '登錄', 'url' => ['/member/login']]
) : '',
!\Yii::$app->user->isGuest ? (
'歡迎您回來,' . \Yii::$app->user->identity->username .
Html::a('退出', ['member/logout'])
) : '',
],
]);
NavBar::end();
?>
- 用戶的退出操作:
- 不用清除 session,使用 user 組件的 logout() 自動清除
public function actionLogout(){
Yii->$app->user->logout();
return $this->goBack(Yii:$app->request->referer);
}
3. 過濾器 AccessControl 控制認證用戶;
if(Yii->$app->user->isGuest){
return $this->redirect(['member/auth']);
}
$userid = Yii::$app->user->id;
$orders = Order::getProducts($userid);
public function behaviors(){
return [
'access' => [
'class' => \yii\filters\AccessControl::className(),
'only' => ['*'],
'except' => [],
'rules' => [
[
'allow' => false,
'action' => ['index', 'check'],
'roles' => ['?'],
],
[
'allow' => true,
'action' => ['index', 'check'],
'roles' => ['@'],
]
]
]
];
}
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
'loginUrl'=> ['/member/auth']
],
- 也可以把 behaviors() 提取出來單獨放到一個父類裏面,所有 controller 繼承這個父類
<?php
namespace app\controllers;
use app\models\User;
use app\models\Product;
use Yii;
class CommonController extends Controller{
protected $actions = ['*'];
protected $except = [];
protected $mustLogin = [];
protected $verbs = [];
public function behaviors(){
return [
'access' => [
'class' => \yii\filters\AccessControl::className(),
'only' => $this->actions,
'except' => $this->except,
'rules' => [
[
'allow' => false,
'action' => empty($this->mustLogin) ? [] : $this->mustLogin,
'roles' => ['?'],
],
[
'allow' => true,
'action' => empty($this->mustLogin) ? [] : $this->mustLogin,
'roles' => ['@'],
]
]
],
'verbs' => [
'class' => \yii\filters\VerbFilter::className(),
'actions' => $this->verbs,
],
];
}
}
class XxController extends CommonController{
protected $mustLogin = ['index', 'check', 'add', 'pay', 'received'];
protected $verbs = [
'confirm' => ['post'],
];
}
4. 過濾器 VerbFilter 過濾請求方式;
- VerbFilter 的作用
- 在一些方法當中,比如做了 POST 驗證 Yii::$app->request->isPost,方法只能是 POST 請求,如果是 GET 請求就報錯,返回一個異常
- 如果現在要用過濾器做,同樣可以通過 behavior() 操作
if(!Yii::$app->request->isPost){
throw new \Exception();
}
public function behaviors(){
return [
'access' => [
'class' => \yii\filters\AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => \yii\filters\VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
5. 分離前後臺用戶認證;
6. 後臺使用過濾器驗證用戶;
7. 哈希算法 bcrypt 對密碼加密處理。
- 相關概念
- 數據表存儲密碼,一般不會存取明文,而是通過某種哈希方式對密碼進行哈希化存儲。
- 哈希方式:MD5,SHA1
- 但是隨着現代硬件的發展,黑客可以在短時間內對 MD5 或者 SHA1 哈希化後的密碼進行暴力破解。所以需要更加安全的算法。這種算法叫 bcrypt
- Yii2 框架提供了兩個方法幫助使用 bcrypt 對密碼進行哈希算法
- bcrypt 相關方法
Yii->$app->getSecurity()->generatePasswordHash($password)
:可以將用戶輸入的密碼進行 bcrypt 哈希,然後再存儲到數據表中
Yii->$app->getSecurity()->validatePassword($password, $hash)
:對密碼驗證
$this->userpass = md5($this->userpass);
$this->userpass = Yii->$app->getSecurity()->generatePasswordHash($this->userpass);
$pass = Yii->$app->getSecurity()->validatePassword($this->userpass, $data->userpass);