環境
- PHP 5.5.24
- CakePHP 3.0.11
やりたいこと
-
CakePHP3でログイン管理する(Auth)
-
CakePHP3でロール毎に権限を管理する(ACL)
- Cakeのビュー画面でACLの権限を選択できるようにする(CakePhp3-AclManager、CakePHP Acl Pluginプラグインを使う)
アプリケーションの準備
-
CakePHP3をインストールする。config/app.phpのDBを設定する
-
ユーザーとグループのテーブルを作成する
- 下記参考ページのようなusersとgroupsのテーブルを用意する
- テーブル参考: CakePHP2 アプリケーション準備 ※passwordの長さを60に修正する
- 下記参考ページのようなusersとgroupsのテーブルを用意する
-
usersとgroupsをbake allする
- bakeコマンド参考: Code Generation with Bake
プラグインをインストール
-
インストールするプラグイン
-
composer.jsonに「"cakephp/acl": "dev-master"」「"jcpires/cakephp3-aclmanager": "dev-master"」を追記しcomposer updateする
"require": {
"php": ">=5.4.16",
"cakephp/cakephp": "~3.0",
"mobiledetect/mobiledetectlib": "2.*",
"cakephp/migrations": "~1.0",
"cakephp/plugin-installer": "*",
+ "cakephp/acl": "dev-master",
+ "jcpires/cakephp3-aclmanager": "dev-master"
},
Configの設定
- config/bootstrap.phpの適当な箇所に「Plugin::load('Acl', ['bootstrap' => true]);」を追記する
Controllerの設定
AppController.php
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Controller\ComponentRegistry;
use Cake\Event\Event;
use Acl\Controller\Component\AclComponent;
class AppController extends Controller
{
public function beforeFilter(Event $event)
{
// グループ、ユーザー登録後コメントアウトする
$this->Auth->allow();
}
public function initialize()
{
parent::initialize();
$this->loadComponent('Acl.Acl');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authorize' => 'Controller',
// 権限無しページに飛ぶと無限ループになったり、変なURLにリダイレクトされるのを防ぐ
'unauthorizedRedirect' => false,
'authError' => 'アクセス権限がありません',
//'loginRedirect' => [
// 'controller' => 'Users',
// 'action' => 'index'
//],
]);
}
public function isAuthorized($user)
{
$Collection = new ComponentRegistry();
$acl = new AclComponent($Collection);
$controller = $this->request->controller;
$action = $this->request->action;
return $acl->check(['Users' => ['id' => $user['id']]], "$controller/$action");
}
}
GroupsController.php
- 下記を追記
use Cake\Event\Event;
use JcPires\AclManager\Event\PermissionsEditor;
public $helpers = [
'AclManager' => [
'className' => 'JcPires/AclManager.AclManager'
]
];
public function add()
{
$group = $this->Groups->newEntity();
if ($this->request->is('post')) {
$group = $this->Groups->patchEntity($group, $this->request->data);
if ($this->Groups->save($group)) {
// デフォルトアクセス全許可にしたい場合は下記のコメントアウトを外す
//if (isset($this->request->data['parent_id'])) {
// $parent = $this->request->data['parent_id'];
//} else {
// $parent = null;
//}
//$this->eventManager()->on(new PermissionsEditor());
//$perms = new Event('Permissions.addAro', $this, [
// 'Aro' => $group,
// 'Parent' => $parent,
// 'Model' => 'Groups'
//]);
//$this->eventManager()->dispatch($perms);
$this->Flash->success(__('The group has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The group could not be saved. Please, try again.'));
}
}
$this->set(compact('group'));
$this->set('_serialize', ['group']);
}
public function edit($id = null)
{
$group = $this->Groups->get($id, [
'contain' => []
]);
$this->loadComponent('JcPires/AclManager.AclManager');
$EditablePerms = $this->AclManager->getFormActions();
if ($this->request->is(['patch', 'post', 'put'])) {
$group = $this->Groups->patchEntity($group, $this->request->data);
if ($this->Groups->save($group)) {
$this->eventManager()->on(new PermissionsEditor());
$perms = new Event('Permissions.editPerms', $this, [
'Aro' => $group,
'datas' => $this->request->data
]);
$this->eventManager()->dispatch($perms);
$this->Flash->success(__('The group has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The group could not be saved. Please, try again.'));
}
}
$this->set(compact('group', 'EditablePerms'));
$this->set('_serialize', ['group', 'EditablePerms']);
}
UsersController.php
- 下記を追記
use Cake\Event\Event;
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['login']);
}
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
public function logout()
{
return $this->redirect($this->Auth->logout());
}
Modelの設定
Entity/Group.php
- 下記を追記
public function parentNode()
{
return null;
}
Entity/User.php
- 下記を追記
use Cake\ORM\TableRegistry;
use Cake\Auth\DefaultPasswordHasher;
protected function _setPassword($password)
{
return (new DefaultPasswordHasher)->hash($password);
}
public function parentNode()
{
if (!$this->id) {
return null;
}
if (isset($this->group_id)) {
$group_id = $this->group_id;
} else {
$users_table = TableRegistry::get('Users');
$user = $users_table->find('all', ['fields' => ['group_id']])->where(['id' => $this->id])->first();
$group_id = $user->group_id;
}
if (!$group_id) {
return null;
}
return ['Groups' => ['id' => $group_id]];
}
Table/UsersTable.php
- public function initialize内に追記
$this->addBehavior('Acl.Acl', ['type' => 'requester']);
Table/GroupsTable.php
- public function initialize内に追記
$this->addBehavior('Acl.Acl', ['type' => 'requester']);
Templateの設定
Groups/edit.ctp
- Form内に下記を追記
<?php foreach ($EditablePerms as $Acos) :?>
<?php foreach ($Acos as $controllerPath => $actions) :?>
<?php if (!empty($actions)) :?>
<h4><?= $controllerPath ;?></h4>
<?php foreach ($actions as $action) :?>
<?php ($this->AclManager->checkGroup($group, 'App/'.$controllerPath.'/'.$action)) ? $val = 1 : $val = 0 ?>
<?= $this->Form->label('App/'.$controllerPath.'/'.$action, $action);?>
<?= $this->Form->select('App/'.$controllerPath.'/'.$action, [0 => 'No', 1 => 'Yes'], ['value' => $val]) ;?>
<?php endforeach ;?>
<?php endif;?>
<?php endforeach ;?>
<?php endforeach ;?>
Users/login.ctp
- 新規作成
<div class="users form">
<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Please enter your username and password') ?></legend>
<?= $this->Form->input('username') ?>
<?= $this->Form->input('password') ?>
</fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>
ACLプラグインの修正
- /vendor/cakephp/acl/src/AclExtras.phpのrootNodeの値をAppに変更する
+ public $rootNode = 'App';
- public $rootNode = 'controllers';
ACLテーブルの作成
$ bin/cake Migrations.migrations migrate -p Acl
ACOデータの作成
$ bin/cake acl_extras aco_sync
グループとユーザーを作成
- 「http://アプリ名/groups/add」にアクセスしてグループを追加する
- 「http://アプリ名/groups/edit/1」にアクセスして権限を設定する
- 「http://アプリ名/users/add」にアクセスしてユーザーを追加する
動作確認
- 「http://アプリ名/users/logout」にアクセスしてログアウトする
- AppController.phpの「$this->Auth->allow();」をコメントアウトする
- 「http://アプリ名/」にアクセスしてログイン画面にリダイレクトされるか確認する
- 登録したユーザでログインし権限通りのACLができているか確認する
補足
- 新しくControllerやActionを追加した場合は下記のコマンドで更新する
$ bin/cake acl_extras aco_update