Yii2 用户认证体系

6 篇文章 0 订阅

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
// 在 basic/config/web.php 中设置组件:
$config = [
	'components' => [
		// yii\web\User 组件
		'user' => [
			// 指定属性 identityClass,连接 User 组件的实例是谁(app\models\User)
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => true,	// 保持自动登录
        ],
	],
];

// 找到 app\models\User
// 打开 basic/models/User.php
// 继承 \yii\web\IdentityInterface
class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface{}

// 打开 basic/controllers/SiteController.php
public function actionLogin() {
	// 判断是否登录
	// 用到了 User 组件相关属性
    if (!Yii::$app->user->isGuest) {
        return $this->goHome();	// 登录状态,返回首页
    }

    $model = new LoginForm();
    // post 数据载入
    if ($model->load(Yii::$app->request->post()) && $model->login()) {
        return $this->goBack();
    }

    $model->password = '';
    return $this->render('login', [
        'model' => $model,
    ]);
}

// 打开 basic/models/LoginForm.php
public function login(){
    if ($this->validate()) {
    	// 调用配置文件里设置好的 user 组件里的 login() 方法
    	// 这个 login() 方法就是 user 组件提供的 login() 方法,不是我们自己写的
    	// login() 方法的第一个参数 $this->getUser() 是我们自己写的,获取用户实例,
    	// 第二个参数保存登录时间(配置文件设置 enableAutoLogin = true)
        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(){
 	// user 组件提供的 logout() 方法,系统会自动清空 cookie 和 session 数据
    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
// controller
// 登录
public function actionAuth(){
	// 实例化 user
	$model = new User;
	if(Yii->$app->request->isPost){
		$post = Yii->$app->request->Post();
		// post 参数传递到 model 里的 login()方法
		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); 
	}
}

// model
public function login($data){
	$this->scenario = "login";
	if($this->load($data) && $this->validate()){
		// 设定保留时长
		$lifetime = $this->rememberMe ? 24*3600 : 0;
		// 实例化 session 实例
		$session = Yii::$app->session;
		// 设置 存储 sessionId 的 cookie 的有效时间
		// 去找到对应的 session 文件
		// 下次打开浏览器的时候,会根据 cookie 存储的 sessionId 找到对应的 seesion 文件
		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 个方法
// model
<?php

namespace app\models;

use yii\db\ActiveRecord;
use Yii;

class User extends ActiveRecord implements \yii\web\IdentityInterface{

	public static function findIdentity($id){
		// 根据主键 id 获取对应的用户实例
		return static::findOne($id);
	}
	
 	public static function findIdentityByAccessToken($token, $type = null){
 		// 不是 RESTful 应用,可以不实现
 		// 如果要实现,可以在数据表中存一个字段(token),根据 token 来查询对应的用户的实例
 		return NULL;
 	}

	public function getId(){
		// 获取用户实例的表的主键 id
		return $this->id;
	}

	public function getAuthKey(){
		// 和 AccessToken 有点像,也是在数据表中存一个字段
		// 当使用 cookie 来做一个登录的时候,要验证 cookie,
		// 可以把对应的 AuthKey 存到数据表里,
		// 然后要使用 cookie 的时候先要验证一下
		// 如果是 session 就不需要使用这个方法
		return '';
	}

	public function validateAuthKey($authKey){
		// 将用户传递进来的 AuthKey 做一个验证,
		// 一致返回 true,不一致返回 false
		return true;
	}

	public function getUser(){
		// 返回用户实例,调用 Yii::$app->user->login() 传递进去给 user 组件,
		// user 组件会连接这个 Model,调用继承 \yii\web\IdentityInterface 接口实现的 5 个方法
		// 5 个方法什么时候调用,你不用管,框架会自己调
		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 组件返回的一些内容进行操作
// layout
<?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() 自动清除
// controller

public function actionLogout(){
	Yii->$app->user->logout();
	return $this->goBack(Yii:$app->request->referer); 
}

3. 过滤器 AccessControl 控制认证用户;

// controller
// 未登录跳转到登录界面
if(Yii->$app->user->isGuest){
	return $this->redirect(['member/auth']); 
}

// 获取 id
$userid = Yii::$app->user->id;
$orders = Order::getProducts($userid);

// 但是,以上只是使用了认证体系,并没有去改变每个方法里都要去写这么一些判断
// 解决方法:需要在控制器类中加入**过滤器**,帮助过滤当前用户是否已经登录
// 在 controller 里加入 behaviors() 方法
// Yii 会自动调用,在所有方法执行之前做相关验证
// 注意:每一个控制器里都要写一个 behaviors()
public function behaviors(){
	// 做访问控制
	return [
		'access' => [
			'class' => \yii\filters\AccessControl::className(),
			'only' => ['*'],	// 对控制器中的哪些方法做验证,针对哪些方法有效
			'except' => [],
			'rules' => [
				[
					'allow' => false,
					'action' => ['index', 'check'],
					'roles' => ['?'], 	// guest,未登录用户不能访问这两个方法
				],
				[
					'allow' => true,
					'action' => ['index', 'check'],
					'roles' => ['@'],	// 登录以后的用户有权访问 index 和 check
				]
			]
		]
	];
}

// 无权访问跳到哪个页面
// 在 basic/config/web.php 中设置 loginUrl
// 这样在 controller 中连跳转都不用设置
 'user' => [
   'identityClass' => 'app\models\User',
    'enableAutoLogin' => true,
    'loginUrl'=> ['/member/auth']
],
  • 也可以把 behaviors() 提取出来单独放到一个父类里面,所有 controller 继承这个父类
// CommonController
<?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' => ['?'], 	// guest,未登录用户不能访问这两个方法
					],
					[
						'allow' => true,
						'action' => empty($this->mustLogin) ? [] : $this->mustLogin,
						'roles' => ['@'],	// 登录以后的用户有权访问 $this->mustLogin
					]
				]
			],
			'verbs' => [	// 过滤器
	            'class' => \yii\filters\VerbFilter::className(),
	            'actions' => $this->verbs,
	        ],
		];
	}
	
}

// 其它 Controller
class XxController extends CommonController{
	// 覆盖基类的 $mustLogin
	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'],	// 只允许 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);

// bcrypt 哈希加密
// 数据表 userpass char(32) 修改为 userpass char(64)
// 创建密码
$this->userpass = Yii->$app->getSecurity()->generatePasswordHash($this->userpass);
// 验证,第一个参数为密码字符串,第二个为 hash 过的字符串,验证成功返回true
$pass = Yii->$app->getSecurity()->validatePassword($this->userpass, $data->userpass);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值