Yii - 验证和授权(Authentication and Authorization)
1. 定义身份类 (Defining Identity Class)
为了验证一个用户,我们定义一个有验证逻辑的身份类。这个身份类实现[IUserIdentity] 接口。
不同的类可能实现不同的验证方式(例如:OpenID,LDAP)。最好是继承 CUserIdentity,此类是居于用户名和密码的验证方式。
定义身份类的主要工作是实现[IUserIdentity::authenticate]方法。在用户会话中根据需要,身份类可能需要定义别的身份信息。
应用实例
下面的例子,我们使用Active Record来验证提供的用户名、密码和数据库的用户表是否吻合。我们通过重写getId函数来返回验证过程中获得的_id变量(缺省的实现则是返回用户名)。在验证过程中,我们还借助CBaseUserIdentity::setState函数把获得的title信息存成一个状态。
- public function authenticate()
- {
- $username=strtolower($this->username);
- $user=User::model()->find('LOWER(user_loginname)=?',array($username));
- if($user===null)
- $this->errorCode=self::ERROR_USERNAME_INVALID;
- else if(!$user->validatePassword($this->password))//! is here mean true to false, f to t
- $this->errorCode=self::ERROR_PASSWORD_INVALID;
- else
- {
- $this->_id=$user->user_id;
- //把用户信息存入SESSION
- $this->setState('user_id', $user->user_id);
- $this->setState('user_name', $user->user_name);
- $this->errorCode=self::ERROR_NONE;
- }
- return $this->errorCode==self::ERROR_NONE;
- }
作为状态存储的信息(通过调用CBaseUserIdentity::setState)将被传递给CWebUser。而后者则把这些信息存放在一个永久存储媒介上(如session)。我们可以把这些信息当作CWebUser的属性来使用。例如,为了获得当前用户的user_name信息,可以使用Yii::app()->user->user_name 。
提示: 缺省情况下,CWebUser用session来存储用户身份信息。如果允许基于cookie方式登录(通过设置 CWebUser::allowAutoLogin为 true),用户身份信息将被存放在cookie中。确记敏感信息不要存放(例如 password) 。
2. 登录和注销(Login and Logout)
使用身份类和用户部件,方便的实现登录和注销。
- // 使用提供的用户名和密码登录用户
- $identity=new UserIdentity($username,$password);
- if($identity->authenticate())
- Yii::app()->user->login($identity);
- else
- echo $identity->errorMessage;
- ......
- // 注销当前用户
- Yii::app()->user->logout();
基于Cookie 的登录
缺省情况下,用户将根据session configuration完成一序列inactivity动作后注销。设置用户部件的allowAutoLogin属性为true和在CWebUser::login方法中设置一个持续时间参数来改变这个行为。即使用户关闭浏览器,此用户将保留用户登陆状态时间为被设置的持续时间之久。前提是用户的浏览器接受cookies。
- // 保留用户登陆状态时间7天
- // 确保用户部件的allowAutoLogin被设置为true。
- Yii::app()->user->login($identity,3600*24*7);
3. 访问控制过滤器(Access Control Filter)
访问控制过滤器是检查当前用户是否能执行访问的controller action的初步授权模式。这种授权模式基于用户名,客户IP地址和访问类型。 It is provided as a filter named as "accessControl".
小贴士 : 访问控制过滤器适用于简单的验证。需要复杂的访问控制,需要使用将要讲解到的基于角色访问控制(role-based access (RBAC)).
在控制器(controller)里重载CController::filters方法设置访问过滤器来控制访问动作(看 Filter 了解更多过滤器设置信息)。
- class PostController extends CController
- {
- ......
- public function filters()
- {
- return array(
- 'accessControl',
- );
- }
- }
在上面,设置的access control过滤器将应用于PostController里每个动作。过滤器具体的授权规则通过重载控制器的CController::accessRules方法来指定。
- class PostController extends CController
- {
- ......
- public function accessRules()
- {
- return array(
- array('deny',
- 'actions'=>array('create', 'edit'),
- 'users'=>array('?'),
- ),
- array('allow',
- 'actions'=>array('delete'),
- 'roles'=>array('admin'),
- ),
- array('deny',
- 'actions'=>array('delete'),
- 'users'=>array('*'),
- ),
- );
- }
- }
上面设定了三个规则,每个用个数组表示。数组的第一个元素不是'allow'就是'deny',其他的是名-值成对形式设置规则参数的。上面的规则这样理解:create和edit动作不能被匿名执行;delete动作可以被admin角色的用户执行;delete动作不能被任何人执行。
访问规则是一个一个按照设定的顺序一个一个来执行判断的。和当前判断模式(例如:用户名、角色、客户端IP、地址)相匹配的第一条规则决定授权的结果。如果这个规则是allow,则动作可执行;如果是deny,不能执行;如果没有规则匹配,动作可以执行。
提示:为了确保某类动作在没允许情况下不被执行,设置一个匹配所有人的deny规则在最后,类似如下:
- return array(
- // ... 别的规则...
- // 以下匹配所有人规则拒绝'delete'动作
- array('deny',
- 'action'=>'delete',
- ),
- );
因为如果没有设置规则匹配动作,动作缺省会被执行。
访问规则通过如下的上下文参数设置:
actions : 设置哪个动作匹配此规则。
users : 设置哪个用户匹配此规则。 此当前用户的name 被用来匹配. 三种设定字符在这里可以用:
*
: 任何用户,包括匿名和验证通过的用户。?
: 匿名用户。@
: 验证通过的用户。
roles : 设定哪个角色匹配此规则。 这里用到了将在后面描述的role-based access control 技术。In particular, the rule is applied if CWebUser::checkAccess returns true for one of the roles.提示,用户角色应该被设置成
allow
规则,因为角色代表能做某些事情。ips : 设定哪个客户端IP匹配此规则。
verbs : 设定哪种请求类型(例如:
GET
,POST
)匹配此规则。expression : 设定一个PHP表达式。它的值用来表明这条规则是否适用。在表达式,你可以使用一个叫
$user
的变量,它代表的是Yii::app()->user
。这个选项是在1.0.3版本里引入的。
授权处理结果(Handling Authorization Result)
当授权失败,即,用户不允许执行此动作,以下的两种可能将会产生:
如果用户没有登录和在用户部件中配置了loginUrl ,浏览器将重定位网页到此配置URL。
否则一个错误代码401的HTTP例外将显示。
当配置loginUrl 属性,可以用相对和绝对URL。还可以使用数组通过CWebApplication::createUrl来生成URL。第一个元素将设置route 为登录控制器动作,其他为名-值成对形式的GET参数。如下,
- array(
- ......
- 'components'=>array(
- 'user'=>array(
- // 这实际上是默认值
- 'loginUrl'=>array('site/login'),
- ),
- ),
- )
如果浏览器重定位到登录页面,而且登录成功,我们将重定位浏览器到引起验证失败的页面。我们怎么知道这个值呢?我们可以通过用户部件的returnUrl 属性获得。我们因此可以用如下执行重定向:
- Yii::app()->request->redirect(Yii::app()->user->returnUrl);
4. 基于角色的访问控制(Role-Based Access Control)
基于角色的访问控制提供了一种简单而又强大的集中访问控制。请参阅维基文章了解更多详细的RBAC与其他较传统的访问控制模式的比较。
Yii 通过其 authManager 组件实现了分等级的 RBAC 结构。在下文中,我们将首先介绍在此结构中用到的主要概念。然后讲解怎样定义用于授权的数据。在最后,我们看看如何利用这些授权数据执行访问检查。
概览(Overview)
在 Yii 的 RBAC 中,一个基本的概念是 授权项目(authorization item)。一个授权项目就是一个做某件事的许可(例如新帖发布,用户管理)。根据其粒度和目标受众,授权项目可分为 操作(operations),任务(tasks) 和 角色(roles)。一个角色由若干任务组成,一个任务由若干操作组成, 而一个操作就是一个许可,不可再分。例如,我们有一个系统,它有一个 管理员 角色,它由 帖子管理 和 用户管理 任务组成。 用户管理 任务可以包含 创建用户,修改用户 和 删除用户 操作组成。为保持灵活性,Yii 还允许一个角色包含其他角色或操作,一个任务可以包含其他操作,一个操作可以包括其他操作。
授权项目是通过它的名字唯一识别的。
一个授权项目可能与一个 业务规则 关联。业务规则是一段 PHP 代码,在进行涉及授权项目的访问检查时将会被执行。仅在执行返回 true 时,用户才会被视为拥有此授权项目所代表的权限许可。例如,当定义一个 updatePost(更新帖子) 操作时,我们可以添加一个检查当前用户 ID 是否与此帖子的作者 ID 相同的业务规则,这样,只有作者自己才有更新帖子的权限。
通过授权项目,我们可以构建一个 授权等级体系 。在等级体系中,如果项目 A 由另外的项目 B 组成(或者说 A 继承了 B 所代表的权限),则 A 就是 B 的父项目。一个授权项目可以有多个子项目,也可以有多个父项目。因此,授权等级体系是一个偏序图(partial-order graph)结构而不是一种树状结构。在这种等级体系中,角色项目位于最顶层,操作项目位于最底层,而任务项目位于两者之间。
一旦有了授权等级体系,我们就可以将此体系中的角色分配给用户。而一个用户一旦被赋予一个角色,他就会拥有此角色所代表的权限。例如,如果我们赋予一个用户 管理员 的角色,他就会拥有管理员的权限,包括 帖子管理 和 用户管理 (以及相应的操作,例如 创建用户)。
现在有趣的部分开始了,在一个控制器动作中,我们想检查当前用户是否可以删除指定的帖子。利用 RBAC 等级体系和分配,可以很容易做到这一点。如下:
- if(Yii::app()->user->checkAccess('deletePost'))
- {
- // 删除此帖
- }
5. 配置授权管理器(Authorization Manager)
在我们准备定义一个授权等级体系并执行访问权限检查之前,我们需要配置一下 authManager 应用组件。 Yii 提供了两种授权管理器: CPhpAuthManager 和 CDbAuthManager。前者将授权数据存储在一个 PHP 脚本文件中而后者存储在数据库中。配置 authManager 应用组件时,我们需要指定使用哪个授权管理器组件类,以及所选授权管理器组件的初始化属性值。例如:
- return array(
- 'components'=>array(
- 'db'=>array(
- 'class'=>'CDbConnection',
- 'connectionString'=>'sqlite:path/to/file.db',
- ),
- 'authManager'=>array(
- 'class'=>'CDbAuthManager',
- 'connectionID'=>'db',
- ),
- ),
- );
然后,我们便可以使用 Yii::app()->authManager 访问 authManager 应用组件。
6. 定义授权等级体系
定义授权等级体总共分三步:定义授权项目,建立授权项目之间的关系,还要分配角色给用户。 authManager 应用组件提供了用于完成这三项任务的一系列 API 。
要定义一个授权项目,可调用下列方法之一,具体取决于项目的类型:
建立授权项目之后,我们就可以调用下列方法建立授权项目之间的关系:
最后,我们调用下列方法将角色分配给用户。
下面的代码演示了使用 Yii 提供的 API 构建一个授权体系的例子:
- $auth=Yii::app()->authManager;
- $auth->createOperation('createPost','create a post');
- $auth->createOperation('readPost','read a post');
- $auth->createOperation('updatePost','update a post');
- $auth->createOperation('deletePost','delete a post');
- $bizRule='return Yii::app()->user->id==$params["post"]->authID;';
- $task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);
- $task->addChild('updatePost');
- $role=$auth->createRole('reader');
- $role->addChild('readPost');
- $role=$auth->createRole('author');
- $role->addChild('reader');
- $role->addChild('createPost');
- $role->addChild('updateOwnPost');
- $role=$auth->createRole('editor');
- $role->addChild('reader');
- $role->addChild('updatePost');
- $role=$auth->createRole('admin');
- $role->addChild('editor');
- $role->addChild('author');
- $role->addChild('deletePost');
- $auth->assign('reader','readerA');
- $auth->assign('author','authorB');
- $auth->assign('editor','editorC');
- $auth->assign('admin','adminD');
建立此授权等级体系后,authManager 组件(例如 CPhpAuthManager, CDbAuthManager)就会自动加载授权项目。因此,我们只需要运行上述代码一次,并不需要在每个请求中都要运行。
信息: 上面的示例看起来比较冗长拖沓,它主要用于演示的目的。 开发者通常需要开发一些用于管理的用户界面,这样最终用户可以通过界面更直观地建立一个授权等级体系。
7. 使用业务规则
在定义授权等级体系时,我们可以将 业务规则 关联到一个角色,一个任务,或者一个操作。我们也可以在为一个用户分配角色时关联一个业务规则。一个业务规则就是一段 PHP 代码,在我们执行权限检查时被执行。代码返回的值用来决定是否将角色或分配应用到当前用户。在上面的例子中,我们把一条业务规则关联到了 updateOwnPost 任务。在业务规则中,我们简单的检查了当前用户的 ID 是否与指定帖子的作者 ID 相同。 $params 数组中的帖子(post)信息由开发者在执行权限检查时提供。
权限检查
要执行权限检查,我们首先需要知道授权项目的名字。例如,要检查当前用户是否可以创建帖子,我们需要检查他是否拥有 createPost 所表示的权限。然后我们调用 CWebUser::checkAccess 执行权限检查:
- if(Yii::app()->user->checkAccess('createPost'))
- {
- // 创建帖子
- }
如果授权规则关联了一条需要额外参数的业务规则,我们也可以传递给它。例如,要检查一个用户是否可以更新帖子,我们可以通过 $params 传递帖子的数据:
- $params=array('post'=>$post);
- if(Yii::app()->user->checkAccess('updateOwnPost',$params))
- {
- // 更新帖子
- }
使用默认角色
注意: 默认角色功能从 1.0.3 版本起可用。
许多 Web 程序需要一些可以分配给系统中所有或大多数用户的比较特殊的角色。例如,我们可能想要分配一些权限给所有已通过身份验证的用户。如果我们特意指定并存储这些角色分配,就会引起很多维护上的麻烦。我们可以利用 默认角色 解决这个问题。
默认角色就是一个隐式分配给每个用户的角色,这些用户包括通过身份验证的用户和游客。我们不需要显式地将其分配给一个用户。当 CWebUser::checkAccess 被调用时,将会首先检查默认的角色,就像它已经被分配给这个用户一样。
默认角色必须定义在 CAuthManager::defaultRoles 属性中。例如,下面的配置声明了两个角色为默认角色:authenticated 和 guest。
- return array(
- 'components'=>array(
- 'authManager'=>array(
- 'class'=>'CDbAuthManager',
- 'defaultRoles'=>array('authenticated', 'guest'),
- ),
- ),
- );
由于默认角色会被分配给每个用户,它通常需要关联一个业务规则以确定角色是否真的要应用到用户。例如,下面的代码定义了两个角色, authenticated 和 guest,很高效地分别应用到了已通过身份验证的用户和游客用户。
- $bizRule='return !Yii::app()->user->isGuest;';
- $auth->createRole('authenticated', 'authenticated user', $bizRule);
- $bizRule='return Yii::app()->user->isGuest;';
- $auth->createRole('guest', 'guest user', $bizRule);
Yii - 验证和授权(Authentication and Authorization)的更多相关文章
- 认证和授权(Authentication和Authorization)
什么是OAuth 如今很多网站的功能都强调彼此间的交互,因此我们需要一种简单,标准的解决方案来安全的完成应用的授权,于是,OAuth应运而生,看看官网对其的定义: An open protocol t ...
- authentication vs authorization 验证与授权的区别
认证和授权的区别 Authentication vs. Authorization简单来说,认证(Authentication )是用来回答以下问题: 用户是谁 当前用户是否真的是他所代表的角色 通常 ...
- Windows Azure Active Directory (1) 前言 - 基于声明的验证和授权
<Windows Azure Platform 系列文章目录> 在我们介绍整套系统架构之前,我们需要首先定义一些基本的概念. 用户及其属性: 用户值得是要使用某项服务的个体.用户一般都有一 ...
- asp.net2.0安全性(3)--验证与授权--转载来自车老师
"验证"与"授权"是对网页资源安全管理的两道门. 验证(Authentication):检查用户是否是合法的用户.就像是网站大门口的保卫,服责验证使用的用户名和 ...
- 使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)——第1部分
原文:使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)--第1部分 原文链接:https://www.codeproject.com/Articles/5160941/ASP- ...
- 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权
OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...
- 学学dotnet core中的身份验证和授权-1-概念
前言 身份验证: Authentication 授权: Authorization net core 中的身份验证和授权这两个部分,是相辅相成的.当初我在学在部分的时候,是看的 net core 官网 ...
- 基于OWIN WebAPI 使用OAUTH2授权服务【授权码模式(Authorization Code)】
之前已经简单实现了OAUTH2的授权码模式(Authorization Code),但是基于JAVA的,今天花了点时间调试了OWIN的实现,基本就把基于OWIN的OAUHT2的四种模式实现完了.官方推 ...
- ASP.NET Web API身份验证和授权
英语原文地址:http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-a ...
随机推荐
- 《C语言学习笔记》指针数组及其应用
C语言中,最灵活但又容易出错的莫过于指针了.而指针数组,是在C中很常见的一个应用.指针数组的意思是说,这个数组存储的所有对象都为指针.除了存储对象为指针,即一个地址外,其它操作和普通数组完全一样. # ...
- ECMAScript一元操作符
在ECMAScript中提供了一元操作符进行简单的运算,一元操作符是ECMAScript中最简单的操作符,它只能对一个值进行操作. 一元操作符有两种类型,一种是递增和递减操作符,一种是一元加和一元减操 ...
- jQuery查看dom元素上绑定的事件列表
jQuery API提供了一种能够查看元素已绑定事件的列表,这个功能在进行功能调试的时候特别有用,尤其确定在代码执行过程中元素绑定的事件是否被更改. 1: jQuery( elem ).dat ...
- jquery 之事件 多库共存(noConflict)
/*jquery 之 简单事件jquery 与其它库共存,提供了 .noConflict() 方法,用法如下<script src="jquery 库"><scr ...
- html定义对象
<object>定义一个对象<param>为对象定义一个参数 参数的名称:name = "" 参数的值:value=""classid: ...
- ubuntu sudo
sudo(substitute user 或者 superuser do),是一种程序, 以允许用户通过安全的方式使用特殊的权限运行程序(通常为系统的超级 用户) 语法 sudo [-bhHpV][- ...
- jquery解析XML(1)
jquery解析XML文件 html代码 <!DOCTYPE html><html><head><title>解析XML</title>&l ...
- js函数与变量同名
console.log(a); var a = 3; function a(){} 输出的结果是:[Function: a] 注意一下几点就能知道原因了! 1)函数声明会置顶2)变量声明也会置顶3)函 ...
- netbeans git 配置(ssh方式)
git出问题了,自己又重新配置了下git. 参考文章: https://netbeans.org/kb/docs/ide/git_zh_CN.html#github
- python下redis的基本操作:
1. 基本操作: >>> import redis >>> print redis.__file__ /usr/local/lib/python2.7/dist-p ...