Yii2 认证实现原理和示例
Yii的用户认证分为两个部分,一个是User组件,负责管理用户认证状态的,包括登录,登出,检测当前登录状态等,源文件位于vender/yiisoft/yii2/web/User.php。另一个是实现接口IdentityInterface的模型,同时必须继承ActiveRecord,当用户登录注册时,组件User会通过模型中的接口方法,对用户进行验证。
对于用户状态切换主要通过switchIdentity方法实现的,比如注册后,用户登录时,会用到User组件中的switchIdentity方法,如下:
/**
* 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中,用户打开新页面时,只需下面的方法就可以判断是否已经登录。
//已登录返回true
Yii::$app->user->isGuest
访问user组件的isGuest属性,会通过魔术方法,调用User组件中的getIsGuest方法
/**
* 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()方法
/**
* 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()更新新用户状态
/**
* 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模型:
<?php
/**
* Created by PhpStorm.
* User: zhenbao
* Date: 16/10/17
* Time: 下午4:14
*/ namespace app\models; use Yii;
use yii\web\IdentityInterface;
use yii\db\ActiveRecord; 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;
}
}
控制器:
<?php namespace app\controllers; use Yii;
use yii\web\Controller;
use app\models\User;
use yii\web\Cookie; class UserController extends Controller
{
/**默认方法为home方法
* @var string
*/
public $defaultAction = 'home'; /**用户登录,当已经登录直接跳转home页面,
* 否则,查看是否有访问accessToken,如果有使用accessToken登录,如果accessToken无效则删除accessToken然后返回到登录界面
* 如果没有accessToken则使用用户的登录密码登录,验证成功后,查看是否选择了记住我,有则生成accessToken,下次直接使用accessToken登录
* 登录失败,返回到登录界面重新登录。
* @return string|\yii\web\Response
*/
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|\yii\web\Response
*/
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 \yii\web\Response
*/
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|\yii\web\Response
*/
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
<?php use yii\helpers\Html;
use yii\bootstrap\ActiveForm; $form = ActiveForm::begin([
'id' => 'user',
'options' => ['class' => 'form-horizontal'],
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<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>
视图注册:
<?php use yii\helpers\Html;
use yii\bootstrap\ActiveForm; $this->title = 'register'; $form = ActiveForm::begin([
'id' => 'login-form',
'options' => ['class' => 'form-horizontal'],
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<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视图:
<?php
echo $user -> username;
?>
<a href="/?r=user/logout">退出</a>
运行:
home页面
文档:http://www.yiichina.com/doc/guide/2.0/security-authentication
Yii2 认证实现原理和示例的更多相关文章
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 ...
- spring原理案例-基本项目搭建 03 创建工程运行测试 spring ioc原理实例示例
下面开始项目的搭建 使用 Java EE - Eclipse 新建一 Dynamic Web Project Target Runtime 选 Apache Tomcat 7.0(不要选 Apache ...
- asp.net core 使用identityServer4的密码模式来进行身份认证(2) 认证授权原理
前言:本文将会结合asp.net core 认证源码来分析起认证的原理与流程.asp.net core版本2.2 对于大部分使用asp.net core开发的人来说. 下面这几行代码应该很熟悉了. s ...
- 图解Kerberos认证工作原理
本文是我在看了这篇英文说明之后的总结 https://technet.microsoft.com/zh-cn/library/cc961976.aspx 是总结,不是翻译,所以是我看后按自己的理解写的 ...
- OAuth认证协议原理分析及同步消息到Twitter和Facebook使用方法
OAuth有什么用?为什么要使用OAuth? twitter或豆瓣用户一定会发现,有时候,在别的网站,点登录后转到 twitter登录,之后转回原网站,你会发现你已经登录此网站了,这种网站就是这个效果 ...
- 格式化字符串攻击原理及示例.RP
格式化字符串攻击原理及示例 一.类printf函数簇实现原理 类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型. 对于这种情况,可以使用省略号指定参数表. 带有省略号的 ...
- silverlight漂亮的文件上传进度显示原理及示例
silverlight漂亮的文件上传进度显示原理及示例 作者:chenxumi 出处:博客园 2009/11/27 13:37:11 阅读 1219 次 概述:在网站根目录web.config里配 ...
- PHP服务器端API原理及示例讲解(接口开发)
http://www.jb51.net/article/136816.htm 下面小编就为大家分享一篇PHP服务器端API原理及示例讲解(接口开发),具有很好的参考价值,希望对大家有所帮助 相信大家都 ...
随机推荐
- python学习2
1.input()返回的是字符串, 如果想读入一个数字,应该用int()转化 2.循环的写法与C不同的地方就是,for while等写完之后在那一行后面要加上一个冒号,这是比较特殊的地方. 还有就是r ...
- JS中typeof与instanceof的区别
JavaScript 中 typeof 和 instanceof 常用来判断一个变量是否为空,或者是什么类型的.但它们之间还是有区别的: Typeof typeof 是一个一元运算,放在一个运算数之前 ...
- [No0000A8]Word中设置图片下的题注及插入多级列表编号
1.什么是题注? 2.怎么实现一个可以自动更新的题注? 只有先定义好文档编号后,才可以设置出正确的图片下标题注. 文章的结构可以通过导航窗口导航. 导航窗口打开方式. 3.设置好文档编号后,怎样插入 ...
- webstorm对WebGL自动提示
默认竟然没有勾选上,怪不得提示的时候,有很多webgl接口找不到方法(虽然可以运行).
- [LeetCode] Gas Station 加油站问题
There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. You ...
- Ubuntu中配置Java环境变量时,出现command not found问题解决记录
百度出Ubuntu中配置Java环境变量时,在利用sudo gedit /etc/profile 对profile编辑后, 在terminal中输入 sudo source /etc/profile, ...
- 2016第七季极客大挑战Writeup
第一次接触CTF,只会做杂项和一点点Web题--因为时间比较仓促,写的比较简略.以后再写下工具使用什么的. 纯新手,啥都不会.处于瑟瑟发抖的状态. 一.MISC 1.签到题 直接填入题目所给的SYC{ ...
- [bigdata] kafka基本命令 -- 迁移topic partition到指定的broker
版本 0.9.2 创建topic bin/kafka-topics.sh --create --topic topic_name --partition 6 --replication-factor ...
- struts-hibernate-ajax完成区县和街道级联下拉框功能
前言:这次dao用的是hibernate,控制层和显示层用的是struts,页面用的是ajax... 啰嗦:我做这个用了很久,用了2周,难点没破解的地方,hibernate的多对一关系生成实体类中属性 ...
- MDK5 STM32编译问题汇总
MDK5 STM32编译问题汇总 WIN8.KEIL-MDK-5 编译时,出现弹窗"The ARM C/C++ Compiler 已停止工作",关闭弹窗后,编译输出的窗口中出现如下 ...