LoginSignup
0
1

More than 3 years have passed since last update.

【備忘録】初めてのCakePHP③ - 認証編

Last updated at Posted at 2021-05-21

開発環境

【備忘録】初めてのCakePHP① - 環境構築編

やりたいこと

新規登録、ログイン(username, password)、ログアウト

実装

/var/www/cake
$ bin/cake bake migration CreateUsers \
username:string[30] \
filename:string[255] \
password:string[255] \
role:string[20] \
created modified
config/Migrations/xxxxxxxxx_CreateUsers.php
$table->addColumn('password', 'string', [
  'default' => null,
  'limit' => 255,
  'null' => true,  // falseからtrueに変える
]);
/var/www/cake
$ bin/cake migrations migrate
$ bin/cake bake model Users

モデル

src/Model/Table/UsersTable.php
class UsersTable extends Table
{
  /**
   * Initialize method
   *
   * @param array $config The configuration for the Table.
   * @return void
   */
  public function initialize(array $config)
  {
    parent::initialize($config);

    $this->setTable('users');
    $this->setDisplayField('id');
    $this->setPrimaryKey('id');

    $this->addBehavior('Timestamp');

    $this->hasMany('Articles', [
      'foreignKey' => 'user_id'
    ]);
  }

  public function validationDefault(Validator $validator)
  {
    $validator
      ->integer('id')
      ->allowEmptyString('id', null, 'create');

    $validator
      ->scalar('username')
      ->maxLength('username', 30)
      ->requirePresence('username', 'create')
      ->notEmptyString('username');

    $validator
      ->scalar('filename')
      ->maxLength('filename', 255)
      ->notEmptyString('filename');

    $validator
      ->scalar('password')
      ->maxLength('password', 255)
      ->requirePresence('password', 'create')
      ->notEmptyString('password');

    $validator
      ->scalar('role')
      ->maxLength('role', 20)
      ->requirePresence('role', 'create')
      ->notEmptyString('role');

    return $validator;
  }

パスワードはハッシュ化して保存

src/Model/Entity/User.php
  protected function _setPassword($password)
  {
    if (mb_strlen($password) > 0) {
      return (new DefaultPasswordHasher)->hash($password);
    }
  }

コントローラ

/var/www/cake
$ bin/cake bake controller UsersController
src/Controller/AppController.php
  public function initialize()
  {
    parent::initialize();

    $this->loadComponent('RequestHandler', [
      'enableBeforeRedirect' => false,
    ]);
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
      'authorize' => ['Controller'],
      // 認証後のリダイレクト先
      'loginRedirect' => [
        'controller' => 'Articles',
        'action' => 'index'
      ],
      // ログアウト後のリダイレクト先
      'logoutRedirect' => [
        'controller' => 'Users',
        'action' => 'login'
      ]
    ]);
    /*
     * Enable the following component for recommended CakePHP security settings.
     * see https://book.cakephp.org/3/en/controllers/components/security.html
     */
    //$this->loadComponent('Security');
  }

  public function beforeFilter(Event $event)
  {
    // index, showメソッドではログイン不要
    $this->Auth->allow(['index', 'show', 'display']);
  }
src/Controller/UsersController.php
<?php

namespace App\Controller;

use App\Controller\AppController;
use Cake\Collection\Collection;
use Cake\Event\Event;
use Cake\Filesystem\Folder;
use Cake\Filesystem\File;

/**
 * Users Controller
 *
 * @property \App\Model\Table\UsersTable $Users
 *
 * @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
 */
class UsersController extends AppController
{

  public function beforeFilter(Event $event)
  {
    parent::beforeFilter($event);
    $this->Auth->allow(['register', 'logout']);
  }

  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(__('ログインIDまたはパスワードが一致しませんでした'));
    }
  }

  public function logout()
  {
    return $this->redirect($this->Auth->logout());
  }

  /**
   * Register method
   *
   * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
   */
  public function register()
  {
    $user = $this->Users->newEntity();
    if ($this->request->is('post')) {
      $user = $this->Users->patchEntity($user, $this->request->getData());
      if ($this->Users->save($user)) {
        $this->Flash->success(__('ユーザーが保存されました。'));
        return $this->redirect(['controller' => 'articles', 'action' => 'index']);
      }
      $this->Flash->error(__('ユーザーの保存に失敗しました。 お手数ですが、再度お試しください。'));
    }
    $this->set(compact('user'));
  }

}
ビュー

ログイン画面

src/Template/Users/login.ctp
<div class="form my-4">
  <?= $this->Flash->render() ?>
  <fieldset class="card p-5">
    <?= $this->Form->create(); ?>
    <legend class="mb-5"><?= __('ログイン') ?></legend>
    <div class="form-outline input text mb-5">
      <input type="text" name="username" id="username" value="<?= $this->request->getData('username') ?? ''; ?>" class="form-control" />
      <label class="form-label" for="username">ログインID</label>
    </div>
    <div class="form-outline input text mb-5">
      <input type="password" name="password" id="password" value="<?= $this->request->getData('password') ?? ''; ?>" class="form-control" />
      <label class="form-label" for="password">パスワード</label>
    </div>
    <div class="text-center">
      <?= $this->Form->button(__('ログイン'), [
        'class' => 'btn btn-primary btn-rounded'
      ]); ?>
    </div>
    <?= $this->Form->end(); ?>
  </fieldset>
</div>

新規登録

src/Template/Users/register.ctp
<div class="form my-4">
  <?= $this->Flash->render() ?>
  <fieldset class="card p-5">
    <?= $this->Form->create(); ?>
    <legend class="mb-5"><?= __('新規登録') ?></legend>
    <div class="form-outline mb-5">
      <input type="text" name="username" id="username" value="<?= $this->request->getData('username') ?? ''; ?>" class="form-control" />
      <label class="form-label" for="username">ログインID</label>
    </div>
    <div class="form-outline mb-5">
      <input type="password" name="password" id="password" value="<?= $this->request->getData('password') ?? ''; ?>" class="form-control" />
      <label class="form-label" for="password">パスワード</label>
    </div>
    <div class="col-12 mb-5">
      <label class="visually-hidden" for="inlineFormSelectPref">権限</label>
      <select name="role" class="form-select">
        <option value="admin">管理者</option>
        <option value="author">一般</option>
      </select>
    </div>
    <div class="text-center">
      <?= $this->Form->button(__('登録'), [
        'class' => 'btn btn-primary btn-rounded'
      ]); ?>
    </div>
    <?= $this->Form->end(); ?>
  </fieldset>
</div>
ロール
  • 記事の追加は認証者全員ができる
  • 記事の編集、削除は所有者ができる
  • Adminロールのユーザは所有者でなくても記事の編集、削除ができる
src/Model/Table/ArticlesTable.php
public function isOwnedBy($articleId, $userId)
{
  return $this->exists(['id' => $articleId, 'user_id' => $userId]);
}
src/Controller/AppController.php

  public function initialize()
  {
    parent::initialize();

    $this->loadComponent('RequestHandler', [
      'enableBeforeRedirect' => false,
    ]);
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
      'authorize' => ['Controller'],
      // 認証後のリダイレクト先
      'loginRedirect' => [
        'controller' => 'Articles',
        'action' => 'index'
      ],
      // ログアウト後のリダイレクト先
      'logoutRedirect' => [
        'controller' => 'Users',
        'action' => 'login'
      ]
    ]);
    /*
     * Enable the following component for recommended CakePHP security settings.
     * see https://book.cakephp.org/3/en/controllers/components/security.html
     */
    //$this->loadComponent('Security');
  }

  public function beforeFilter(Event $event)
  {
    // index, showメソッドではログイン不要
    $this->Auth->allow(['index', 'show', 'display']);
  }

  // 追加
  public function isAuthorized($user)
  {
    if (isset($user['role']) && $user['role'] === 'admin') {
      return true;
    }
    return false;
  }
src/Controller/ArticlesController.php
class ArticlesController extends AppController
{
  public function isAuthorized($user)
  {
    if (in_array($this->request->getParam('action'), ['add'])) {
      return true;
    }

    if (in_array($this->request->getParam('action'), ['edit', 'delete'])) {
      $articleId = (int)$this->request->getParam('pass')[0];
      if ($this->Articles->isOwnedBy($articleId, $user['id'])) {
        return true;
      }
    }
    return parent::isAuthorized($user);
  }
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1