简单的身份验证和授权应用

接着我们blog教程的例子,如果我们想要建立一个根据登录的用户身份来决定其安全访问到正确的urls。

同时我们还有其他的需求: 允许我们的blog有多个作者,每一个作者都可以自由创作他们自己的posts,编辑和删除它们,而不允许对别人的posts做任何的改动。

创建所有用户的相关代码

首先,让我们在数据库中新建一个表来保存用户的数据

 CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
password VARCHAR(50),
role VARCHAR(20),
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);

我们坚持遵循 CakePHP 的命名约定,同时我们也利用了其他约定:比如在表user中使用username和password, CakePHP 将会自动配置好实现用户登录的大部分工作。

下一步是创建User模型,响应寻找,保存和验证任何用户的数据


 // app/Model/User.php
class User extends AppModel {
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin', 'author')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
)
);
}

让我们也建立我们的控制器 UsersController, 下面的内容是使用 CakePHP 捆绑的代码生成工具生成的基本的UsersController类


 // app/Controller/UsersController.php
class UsersController extends AppController { public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('add');
} public function index() {
$this->User->recursive = 0;
$this->set('users', $this->paginate());
} public function view($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
$this->set('user', $this->User->read(null, $id));
} public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
} public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
} else {
$this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']);
}
} public function delete($id = null) {
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->User->delete()) {
$this->Session->setFlash(__('User deleted'));
$this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('User was not deleted'));
$this->redirect(array('action' => 'index'));
}
}


同样的,我们使用代码生成工具,创建blog的posts的视图。为了教学目的,这里仅展示视图add.ctp:


 <!-- app/View/Users/add.ctp -->
<div class="users form">
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend><?php echo __('Add User'); ?></legend>
<?php echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('role', array(
'options' => array('admin' => 'Admin', 'author' => 'Author')
));
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>


身份验证 (登录 和 登出)

我们现在已经准备好添加我们的认证层了。

在 CakePHP 中,这个功能是由 AuthComponent 完成的,这个组件会为特定动作要求用户登录,处理用户登录和登出,并且检查用户是否有权限进行相应动作(即访问特定页面)。

添加这个组件到应用中,打开 app/Controller/AppController.php 文件,添加如下代码

 // app/Controller/AppController.php
class AppController extends Controller {
//... public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'posts', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'pages', 'action' => 'display', 'home')
)
); public function beforeFilter() {
$this->Auth->allow('index', 'view');
}
//...
}

这里没有什么需要配置的,因为我们前面遵循了user表的命名约定,我们只设置了登录后和登出后页码转到的urls,在我们的例子中,分别是 /posts/ 和 / 。

我们在 `` beforeFilter`` 中所做的功能是告诉组件 AuthComponent,在控制器中的所有 index 和 view 行动都不需要登录。我们希望我们的访问者能够读取和列出posts,而不需要注册网站。

现在,我们需要实现新用户的注册。保存它们的用户名和密码,而更重要的是,在我们的数据库中保存用户的hash过的密码而不是用普通文本形式保存,让我们告诉 AuthComponent 组件让未验证的用户访问用户添加函数并实现登录和登出动作

// app/Controller/UsersController.php

public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('add'); // Letting users register themselves
} public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash(__('Invalid username or password, try again'));
}
}
} public function logout() {
$this->redirect($this->Auth->logout());
}

散列密码还没有做,打开User模型 app/Model/User.php 添加代码

// app/Model/User.php
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel { // ... public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
} // ...

现在,每次用户密码保存的时候,都会使用 AuthComponent 组件提供的默认的类进行散列化。为登录创建模板视图 :

<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend><?php echo __('Please enter your username and password'); ?></legend>
<?php echo $this->Form->input('username');
echo $this->Form->input('password');
?>
</fieldset>
<?php echo $this->Form->end(__('Login')); ?>
</div>

现在你可以访问 /users/add 地址来注册一个新的用户了。注册完成后访问 /users/login 地址登录, 试试访问其他地址比如像/posts/add 这些没有明确允许的地址,你会看到应用会自动的转向到登录页面。

就是这!简单到不可思议。让我们返回去稍微解释下。 beforeFilter 函数告诉AuthComponent组件在UsersController中对 add 动作不需要登录,并且在AppController中的 beforeFilter 也已经设置所有的控制器的``index`` and view 动作都是可以不登录的。

login 动作执行AuthComponent中的 $this->Auth->login() 函数且不需要其他的设置的原因是我们遵循了之前提到的在数据库中的user表的命名约定,并且使用表单提交用户的数据到控制器。这个函数返回登录成功还是失败,如果成功,就重定向到我们设置的登录成功的跳转页面。

登出函数只需要访问 /users/logout 并且重定向到先前配置的 logoutUrl。这个url是 AuthComponent::logout() 函数返回登出成功后的跳转的页面。

权限(谁可以访问什么)

前面已经说了,我们要把这个blog应用改为可以多个用户创作的工具,为了做到这个,我们需要修改posts表,添加对User模型的引用

ALTER TABLE posts ADD COLUMN user_id INT(11);

同时,在PostsController中对新增的post做改动,添加当前登录的用户为作者

   // app/Controller/PostsController.php
public function add() {
if ($this->request->is('post')) {
$this->request->data['Post']['user_id'] = $this->Auth->user('id'); //Added this line
if ($this->Post->save($this->request->data)) {
$this->Session->setFlash('Your post has been saved.');
$this->redirect(array('action' => 'index'));
}
}
} ``user()`` 函数提供由组件提供,返回当前登录用户的所有列的数据.我们使用这个方法获得所需的用户信息。

让我们增强应用的安全性,避免用户编辑或删除其他用户的posts,基本的规则是管理用户可以访问任何的url地址,当前的用户(作者角色)只可以访问到允许的地址。打开 AppController 类,在 Auth 的配置中增加更多选项

// app/Controller/AppController.php

public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'posts', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'pages', 'action' => 'display', 'home'),
'authorize' => array('Controller') // Added this line
)
); public function isAuthorized($user) {
// Admin can access every action
if (isset($user['role']) && $user['role'] === 'admin') {
return true;
} // Default deny
return false;
}

我们只创建了一个非常简单的权限机制。在这个例子中用户登录后角色是``admin`` 的将可以访问任何地址,而其余的(例如角色author ) 同未登录的用户一样不能够做任何事。

这并不是我们所想要的,所以我们需要在我们的 isAuthorized() 方法中支持更多的规则. 与其在 AppController中设置, 不如委托每个控制器提供这些额外的规则。我们要在PostsController中增加规则,允许作者创建posts并且防止其他作者对其post做改动。打开PostsController.php 并添加如下内容

// app/Controller/PostsController.php

public function isAuthorized($user) {
// All registered users can add posts
if ($this->action === 'add') {
return true;
} // The owner of a post can edit and delete it
if (in_array($this->action, array('edit', 'delete'))) {
$postId = $this->request->params['pass'][0];
if ($this->Post->isOwnedBy($postId, $user['id'])) {
return true;
}
} return parent::isAuthorized($user);
}

我们现在重写了 AppController 的 isAuthorized() 方法并且在父类中已核准用户后再进行内部检查,如果他不是,只允许他访问add动作, 并有条件访问edit 和 delete动作. 在 Post 模型中调用 isOwnedBy() 来告诉用户是否有权限来编辑post. 尽量把逻辑挪到模型中是个很好的实践。让我们实现它

// app/Model/Post.php

public function isOwnedBy($post, $user) {
return $this->field('id', array('id' => $post, 'user_id' => $user)) === $post;
}

简单的身份验证和授权教程到这里就结束了。可以参考我们在PostsController中所做的用到UsersController中,你应该也会更具创作性并可根据你自己的规则在 AppController 添加一般规则。

更多信息,参阅完整的Auth指导 Authentication 授权认证 ,这里你可以找到更多组件配置,创建自主的权限类等

CakePHP的blog教程三的更多相关文章

  1. Laravel教程 三:视图变量传递和Blade

    Laravel教程 三:视图变量传递和Blade 此文章为原创文章,未经同意,禁止转载. Blade 上一篇我们简单地说了Router,Views和Controllers的工作流程,这一次我就按照上一 ...

  2. Elasticsearch入门教程(三):Elasticsearch索引&映射

    原文:Elasticsearch入门教程(三):Elasticsearch索引&映射 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文 ...

  3. RabbitMQ入门教程(三):Hello World

    原文:RabbitMQ入门教程(三):Hello World 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...

  4. CRL快速开发框架系列教程三(更新数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  5. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(三)-使用Travis自动部署Hexo(1)

    前言 前面两篇文章介绍了在github上使用hexo搭建博客的基本环境和hexo相关参数设置等. 基于目前,博客基本上是可以完美运行了. 但是,有一点是不太好,就是源码同步问题,如果在不同的电脑上写文 ...

  6. 无废话ExtJs 入门教程三[窗体:Window组件]

    无废话ExtJs 入门教程三[窗体:Window组件] extjs技术交流,欢迎加群(201926085) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3 ...

  7. CocoStudio教程三:认识并利用CocoStudio的果实 运行2.2.1版本

    原文:CocoStudio教程三:认识并利用CocoStudio的果实 原文用的老版,用2.21搞起来好像有些问题,然后自己摸索了下,有的都是乱找的方法,只求能运行... 1,原文的CCJsonRea ...

  8. Android Studio系列教程三--快捷键

    Android Studio系列教程三--快捷键 2014 年 12 月 09 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzhang.com/ ...

  9. NGUI系列教程三

    接下来我们再来看Progress Bar和Slider,对比参数我们可以发现,Progress Bar和slider的明显区别在于slider多一个Thumb选项,这里的Thumb就是我们拖动的时候点 ...

随机推荐

  1. ASP.NET(C#)常用数据加密和解密方法汇总

    一.            数据加密的概念 1.  基本概念 2.  基本功能 3.  加密形式 二.            数据加密的项目应用和学习 1.  媒体加密:DRM 2.  文件加密:文本 ...

  2. javascript学习代码--点击按钮显示内容

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. android的原理,为什么不需要手动关闭程序

    转自android的原理,为什么不需要手动关闭程序 不用在意剩余内存的大小,其实很多人都是把使用其他系统的习惯带过来来了. Andoird大多应用没有退出的设计其实是有道理的,这和系统对进程的调度机制 ...

  4. DUILIB创建不规则窗体,自定义控件(很不错的几十篇文章)

    http://blog.csdn.net/harvic880925/article/details/8925650 http://blog.csdn.net/harvic880925/article/ ...

  5. 【HDOJ】1043 Eight

    这道题目最开始做的时候wa+TLE.后面知道需要状态压缩,最近A掉.并且练习一下各种搜索算法. 1. 逆向BFS+康拓展开. #include <iostream> #include &l ...

  6. 嵌入式系统烧写uboot/bootloader/kernel的一般方法

    嵌入式系统烧写uboot/bootloader/kernel的一般方法   本文介绍了在嵌入式系统中烧写uboot/bootloader/kernel 的一般方法,以及如果uboot或者内核出现错误, ...

  7. 最棒的Visual Studio扩展

    isual Studio是微软公司推出的开发环境,Visual Studio可以用来创建Windows平台下的Windows应用程序和网络应用程序,也可以用来创建网络服务.智能设备应用程序和Offic ...

  8. hdoj 2196 Computer【树的直径求所有的以任意节点为起点的一个最长路径】

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  9. .\Obj\main.axf: Error: L6406E: No space in execution regions with .ANY selector matching sin_i.o(.co

    这个问题原因是 芯片的 空间不足 解决方法是  在KEIL 的DEVICE中选择 更大的空间的芯片型号

  10. XSS 简单理解

    什么是XSS? XSS(Cross Site Scripting),即跨站脚本攻击,是一种常见于web application中的计算机安全漏洞.XSS通过在用户端注入恶意的可运行脚本,若服务器端对用 ...