ホームページ >バックエンド開発 >PHPチュートリアル >CakePHP アクセス制御リスト: 使用ガイド
CMS を構築している場合は、さまざまな権限レベルを持つさまざまなユーザー ロール (スーパーユーザー、管理者、ユーザー) が必要になる場合があります。コードが複雑すぎますか? CakePHP の ACL (アクセス制御リスト) を入力します。適切に設定すれば、たった 1 行でユーザーの権限を確認できます。
ACL を使用すると、それぞれの役割を持つユーザーの階層を作成できます。以下に簡単な例を示します。
この階層を通じて、各ロールに複数の権限を割り当てることができます:
さて、反対側には、アクセス制御オブジェクト (ACO) があります。これらはすべて制御対象です。上で投稿とユーザーについて説明しました。通常、これらのオブジェクトはモデルに直接リンクされているため、Post モデルがある場合は、そのモデルの ACO が必要になります。
各 ACO には、作成、読み取り、更新、削除という 4 つの基本的な権限があります。キーワード
CRUDを使用してそれらを覚えておくことができます。 5 番目の権限であるアスタリスクは、完全アクセスへのショートカットです。 このチュートリアルでは、Post と User の 2 つの ACO のみを使用しますが、必要なだけ ACO を作成できます。
ACLテーブル
ディレクトリの db_acl.sql
にあります。
リーリー
これで ARO ノードと ACO ノードの作成を開始できるようになりましたが、ユーザーがいません。基本的な認証システムを作成する必要があります。
MySQL テーブル:
リーリー ユーザーモデル (models/user.php)
リーリー
(controllers/users_controller.php)
リーリー
新しい要素を取得したので、それらを確認しましょう。まず、
変数を設定します。この変数には、配列内のすべてのコンポーネントが含まれます。 HTML やフォーム ヘルパーと同じようにコア コンポーネントである Auth
コンポーネントが必要になりますが、Cake にはデフォルトで含まれていないため、手動で含める必要があります。
Auth コンポーネントは、いくつかの基本的な認証メカニズムを処理します。これは、ユーザーのログインを支援し、認証されたユーザー セッションを処理するだけでなく、ゲストのログアウトや基本的な認証も処理します。さらに、パスワードを自動的にハッシュ化します。各関数の呼び出し方法については次の段落で説明します。
次に、
beforeFilter という関数を作成します。これは、すべてのコントローラー ロジックを処理する前にいくつかのアクションを設定できるコールバック関数です。 Auth コンポーネントでは、使用するモデル (この場合はユーザー) を指定する必要があります。その後、デフォルトで、ログインしていないすべてのユーザーのアクセスが拒否されます。パラメータが必要な allow()
を使用してこの動作をオーバーライドする必要があります。このパラメーターはアスタリスクにすることができ、認証されていないユーザーがコントローラー内のすべてのメソッドにアクセスできることを指定します。あるいは、認証されていないユーザーがアクセスできる関数を含む配列を渡すこともできます。この場合、関数は 3 つだけなので、次の行は同じになります。
リーリー
関数の場合、Auth コンポーネントがすべてのログイン メカニズムを処理します。ユーザー名とパスワードという 2 つのキーを含む配列を関数に提供するだけです。これらのキーは変更できますが、デフォルトでは、username
フィールドと password
フィールドの両方がデータベースと照合され、ユーザーが認証されている場合は true を返します。
最後に、コントローラーのログイン機能は、ユーザー名とパスワードの組み合わせをデータベースと照合しようとします。
请注意,此代码非常基础。您需要验证用户名字符、用户名是否存在、密码的最小长度等等。
注册视图 (views/users/register.ctp
)
<h2>Register your account</h2> <form method="POST" action="<?=$this->here; ?>"> <p> Username <?=$form->text('User.username'); ?> </p> <p> Password <?=$form->password('User.password'); ?> </p> <?=$form->submit('Register'); ?> </form>
登录视图 (views/users/login.ctp
)
<h2>Log in to your account</h2> <form method="POST" action="<?=$this->here; ?>"> <?=$form->error('User.username'); ?> <p> Username <?=$form->text('User.username'); ?> </p> <p> Password <?=$form->password('User.password'); ?> </p> <?=$form->submit('Log in'); ?> </form>
在网络浏览器中打开 /users/register
并注册一个新帐户。我建议 admin
作为用户名,123
作为密码,如果您的会话过期,只需转到 /users/login
并输入您刚刚创建的正确用户名/密码组合。
我们甚至没有使用 ACL,但我们已经可以拒绝发布、编辑和删除帖子。打开您的 Posts 控制器并添加 Auth 组件。
var $components = array('Auth');
现在在您的网络浏览器中访问 /posts
。如果您已登录,您应该会看到这些帖子,但如果您没有登录,您将被重定向到 /users/login
。通过简单地包含身份验证组件,默认情况下,所有操作都会拒绝来宾。我们需要拒绝未经授权的用户的三种操作:创建、编辑和删除。换句话说,我们必须允许索引和视图。
function beforeFilter(){ $this->Auth->userModel = 'User'; $this->Auth->allow(array('index', 'view')); }
去编辑或创建帖子;如果您尚未登录,您应该被重定向到 /users/login
。一切似乎都进展顺利,但是视图呢?编辑和删除链接正在向所有人显示。我们应该提出一个条件。
但在讨论之前,让我们看看 Auth 的 user() 函数是如何工作的。将这些行复制并粘贴到索引函数中。
$user = $this->Auth->user(); pr($user);
在浏览器中打开 /posts
,如果登录,则 pr()
将抛出类似这样的内容。
Array ( [User] => Array ( [id] => 1 [username] => admin ) )
user()
函数返回一个数组,就像模型一样。如果我们有超过三个字段(不包括密码),它们将显示在数组中。如果您没有登录,该数组将为空,因此如果 Auth 的 user()
数组不为空,您就可以知道用户已登录。
现在,Auth 是一个组件,旨在在控制器中使用。我们需要从视图中了解用户是否已登录,最好是通过帮助程序。我们如何在助手中使用组件? CakePHP 是如此出色和灵活,以至于这是可能的。
<? class AccessHelper extends Helper{ var $helpers = array("Session"); function isLoggedin(){ App::import('Component', 'Auth'); $auth = new AuthComponent(); $auth->Session = $this->Session; $user = $auth->user(); return !empty($user); } ?>
将此代码段保存在 views/helpers
中,命名为 access.php
。现在让我们逐行查看代码。首先,我们设置一个 $helpers
变量。帮助程序可以包含其他帮助程序,就像 $components
一样。 Auth 组件需要 Session 组件,但我们无法在助手中访问该组件。幸运的是,我们有一个会话助手,它将帮助我们。
接下来,我们创建一个函数并使用 App::import
,这将让我们导入一个通常我们无法访问的元素。下一行在 $auth
变量中创建 Auth 组件,现在是一个有点肮脏的黑客;由于 Auth 组件读取会话来了解我们是否已登录,因此它需要 Session 组件,但当我们从不应该属于它的位置导入它时,我们必须给它一个新的 Session 对象。最后,我们使用 user()
并将其设置为 $user
,如果变量不为空则返回 true,否则返回 false。
让我们回到帖子控制器并继续添加助手。
var $helpers = array('Access');
现在可以从视图访问访问助手。在 views/posts
中打开 index.ctp
并替换此行。
<small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small>
有了这个。
<? if($access->isLoggedin()): ?><small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small><? endif; ?>
返回网络浏览器,重新加载索引页,如果您已登录,您将看到每个帖子的编辑和删除链接。否则,您将看不到任何内容。
虽然如果您的应用程序只有一两个用户,这就足够了,但如果您开放注册,这还不够。
打开用户控制器并添加 ACL 组件。
var $components = array('Auth', 'Acl');
接下来,让我们创建一个函数来安装 ACO 和 ARO 节点。
function install(){ if($this->Acl->Aro->findByAlias("Admin")){ $this->redirect('/'); } $aro = new aro(); $aro->create(); $aro->save(array( 'model' => 'User', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Super' )); $aro->create(); $aro->save(array( 'model' => 'User', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Admin' )); $aro->create(); $aro->save(array( 'model' => 'User', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'User' )); $aro->create(); $aro->save(array( 'model' => 'User', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Suspended' )); $aco = new Aco(); $aco->create(); $aco->save(array( 'model' => 'User', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'User' )); $aco->create(); $aco->save(array( 'model' => 'Post', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Post' )); $this->Acl->allow('Super', 'Post', '*'); $this->Acl->allow('Super', 'User', '*'); $this->Acl->allow('Admin', 'Post', '*'); $this->Acl->allow('User', 'Post', array('create')); }
通过导入ACL组件,我们可以访问ACO和ARO模型。我们首先创建一个 ARO,它是对象的请求者;换句话说,谁将访问这些对象。这就是用户(因此是模型中的用户字符串)。我们正在创造不同的角色;超级、管理员、用户和暂停。
接下来,我们对 ACO 执行相同的操作,因为我们只有两个对象要管理(帖子和用户),所以我们将创建两个对象,每个对象一个。
现在,快速说明一下。我们为 ACO 和 ARO 保存的数组有四个字段:模型名称、外键(如果您想授予对一个 ARO 的访问权限,例如他创建的帖子,则该字段很有用)、父 ID(稍后将使用)和 是基本的父子节点关系;我们将在这些角色下创建用户)和别名(这是查找对象的快速形式)。
最后,我们使用 ACL 的 allow()
函数。第一个参数是ACO别名;第二,ARO 别名,第三,授予所述 ARO 的权限。超级用户可以完全访问帖子和用户模型,管理员可以完全访问帖子模型,用户只能创建帖子。
在函数的开头,我声明了一个条件来检查 ACO 中是否存在管理员角色。您不想多次安装相同的东西,对吗?这会严重扰乱数据库。
在 Web 浏览器中打开 /users/install
,由于我们没有视图,CakePHP 会抛出错误,但只需检查 MySQL 转储即可。所有关系都已成功创建,是时候处理子节点了。
让我们清理 users
表。打开 phpMyAdmin,选择您的数据库,users
表,然后单击清空。我们将回到用户控制器上的 register()
函数。就在这一行下方:
$this->Auth->login($data);
粘贴此代码:
// Set the user roles $aro = new Aro(); $parent = $aro->findByAlias($this->User->find('count') > 1 ? 'User' : 'Super'); $aro->create(); $aro->save(array( 'model' => 'User', 'foreign_key' => $this->User->id, 'parent_id' => $parent['Aro']['id'], 'alias' => 'User::'.$this->User->id ));
在第一行中,我们创建一个新的 ARO 对象。然后我们将获取将在其中创建用户的父节点。如果数据库中有记录,我们将其设置为用户 ARO,否则,第一个用户应该是 Super。
然后我们保存一个新的ARO;该模型是我们当前正在开发的模型,User
,foreign_key
是我们刚刚创建的最后一条记录的 id。 parent_id
是我们开始的节点;我们只传递 id 和最后的别名。最好将其命名为 Model_name
,然后是分隔符 ::
,然后是标识符。找到它会容易得多。
现在我们完成了。创建四个用户:超级用户、管理员用户、普通用户和暂停用户。我建议使用相同的用户名和密码进行测试。不要忘记,到目前为止,只有超级用户具有 Super 角色;剩下的都是用户!
由于 ACL 是一个组件,因此只能在控制器内访问它。稍后,它也会出现在视野中;但首先要紧的事情。将 ACL 组件包含在 Posts 控制器中,就像我们在 Users 控制器中所做的那样。现在您可以开始检查权限。让我们进入编辑功能并进行快速测试。在该方法的第一行添加此内容。
$user = $this->Auth->user(); if(!$this->Acl->check('User::'.$user['User']['id'], 'Post', 'update')) die('you are not authorized');
ACL 的 check()
函数需要三个参数:ARO、ACO 和操作。去编辑帖子,如果您没有以超级用户身份登录,脚本就会死掉。转到 /users/login
并以超级用户身份访问并返回编辑。您应该能够编辑该帖子。检查下面的 MySQL 转储,看看它的神奇之处。四个数据库查询:这肯定是不可扩展的。
我们有两个问题。首先,两行用于权限检查。其次,ACL 没有被缓存。并且不要忘记所有登录用户都可以看到编辑链接,即使只有超级用户可以使用它。
让我们创建一个新组件。我们称之为 Access。
<?php class AccessComponent extends Object{ var $components = array('Acl', 'Auth'); var $user; function startup(){ $this->user = $this->Auth->user(); } } ?>
将其保存在controllers/components
中,命名为access.php
。该类的 $user
var 将在组件加载时启动,而 startup()
是一个回调函数,因此现在在该类中设置。现在让我们创建 check()
函数。
function check($aco, $action='*'){ if(!empty($this->user) && $this->Acl->check('User::'.$this->user['User']['id'], $aco, $action)){ return true; } else { return false; } }
我们的 check()
方法只需要两个参数:ACO 和操作(可选)。 ARO 将是每个会话的当前用户。操作参数默认为 *
,这是 ARO 的完全访问权限。该函数首先检查 $this->user
是否不为空(这实际上告诉我们用户是否已登录),然后转到 ACL。我们已经介绍过这一点。
我们现在可以将 Access 组件包含在我们的 Posts 控制器中,并只需一行即可检查权限。
if(!$this->Access->check('Post', 'update')) die('you are not authorized');
用更少的代码可以达到相同的结果,但错误消息很难看。您最好将 die()
替换为 CakePHP 错误处理程序:
$this->cakeError('error404');
这可能很难看,但它确实有效。我们必须创建一个帮助程序,使用自定义方法加载组件以供在帮助程序中使用。
在Access组件(controllers/components/access.php
)中添加此函数。
function checkHelper($aro, $aco, $action = "*"){ App::import('Component', 'Acl'); $acl = new AclComponent(); return $acl->check($aro, $aco, $action); }
现在,让我们重写访问助手(views/helpers/access.php
)。
<?php class AccessHelper extends Helper{ var $helpers = array("Session"); var $Access; var $Auth; var $user; function beforeRender(){ App::import('Component', 'Access'); $this->Access = new AccessComponent(); App::import('Component', 'Auth'); $this->Auth = new AuthComponent(); $this->Auth->Session = $this->Session; $this->user = $this->Auth->user(); } function check($aco, $action='*'){ if(empty($this->user)) return false; return $this->Access->checkHelper('User::'.$this->user['User']['id'], $aco, $action); } function isLoggedin(){ return !empty($this->user); } } ?>
beforeRender()
方法是一个回调,类似于组件的startup()
。我们正在加载两个组件,由于它们将在大多数函数中使用,因此最好一次启动所有组件,而不是每次调用方法时手动启动它们。
现在,在 views/posts
中的 index.ctp
视图中,您可以替换此行。
<small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small>
有了这个。
<? if($access->check('Post')): ?><small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small><? endif; ?>
只是不要忘记检查视图和控制器中的权限!
您可以使用 Auth 组件中的 user()
方法访问已登录用户的用户数据。然后您可以访问该数组并获取您想要的信息。但一定有更好的方法。让我们在 Access 组件中添加以下函数。
function getmy($what){ return !empty($this->user) && isset($this->user['User'][$what]) ? $this->user['User'][$what] : false; }
当您需要保存具有 user_id
关系的帖子时,这非常有用。
$this->data['Post']['user_id'] = $this->Access->getmy('id');
在视图中,我们可以使用助手做类似的事情。
function getmy($what){ return !empty($this->user) && isset($this->user['User'][$what]) ? $this->user['User'][$what] : false; }
在模板文件中,您可以执行如下操作,通过用户名向用户打招呼。
Welcome <?=$access->isLoggedIn() ? $access->getmy('username') : 'Guest'; ?>
假设我们需要为用户 #4 切换角色:他需要成为超级用户。因此,User 的 id 是 4,Aro 的 id 是 1。
$user_id = 4; $user_new_group = 1;
现在我们需要找到用户的 Aro 以便修改其父 Aro。
$aro_user = $this->Acl->Aro->find('first', array( 'conditions' => array( 'Aro.parent_id !=' => NULL, 'Aro.model' => 'User', 'Aro.foreign_key' => $user_id ) ) );
最后,如果 $aro_user
变量不为空,让我们更新 Aro.parent_id
字段。
if(!empty($aro_user)){ $data['id'] = $aro_user['Aro']['id']; $data['parent_id'] = $user_new_group; $this->Acl->Aro->save($data); }
请注意,您必须验证用户的 ID 和新 aro 的 ID。如果其中之一不存在,则会在您的 ACL 表中造成混乱。
虽然我们刚刚创建的组件既简单又有用,但每次检查都会查询数据库。目前尚未准备好投入生产。首先,应尽可能少地查询数据库。为了优化这一点,我们应该利用 CakePHP 的缓存。
使用缓存会大大减少负载,但如果我们有十个帖子出现在索引中,并且对于每个帖子,我们都会检查用户是否具有帖子 Aco 的更新权限,框架将读取并解析一个文件返回相同的结果...每个页面加载十次。
这是第二点:类内的变量和一些条件将使工作更轻松,以便重复的请求将仅检查 Cache 一次。
这两个更改都反映在 access_cache.php
中,位于 controllers/components
目录中。因此,请确保下载源代码!
访问控制列表是大多数应用程序需要的基本功能。 CakePHP 有一个很好的实现,但缺乏良好的文档和示例。我希望通过本教程能够解决这两个问题。感谢您的阅读!
以上がCakePHP アクセス制御リスト: 使用ガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。