2023.09.20
【Phalcon】認証機能を追加する

UnsplashRobert Lukemanが撮影した写真

はじめに

今回は認証機能(サインイン、サインアウト)を実装します。

できるだけシンプルにコードを書くため、デザインは最小限にしてあります。

動作確認した環境

OS:Windows11 Home / WSL(Ubuntu20.04)
php:v7.4.33
phalcon:4.0.6
Phalcon DevTools:v4.2.0
DB:mysql v8.0.34

本題

さっそく認証機能を実装していきます。

①ルーティングを追加する

/app/config/router.phpに以下の赤字2行を追記します。

<?php
$router = $di->getRouter();

$router->add("/users/index",    "Users::index");
$router->add("/users/show",     "Users::show");
$router->add("/users/add",      "Users::add");
$router->add("/users/edit",     "Users::edit");
$router->add("/users/delete",   "Users::delete");
$router->add("/users/signin",   "Users::signin");   //追記
$router->add("/users/signout",  "Users::signout");  //追記

$router->handle($_SERVER['REQUEST_URI']);

②ビューを作成する

まず、サインイン用のビューを作成します。

「/app/view/users/signin.phtml」となるよう「signin.phtml」を作成し、以下記述してください。

<h2>users/signin</h2>

<?php
    //ログイン失敗時のエラーメッセージ表示
    if(!empty($errorMessage)){
        echo "<p style='color:red;'>{$errorMessage}</p>";
    }
?>

<button style="background:#aaa;width:100%;" onclick="location.href='/users/add'">ユーザーの新規登録</button>


<form action="#" method="post">
    <p><label for="name">名前</label></p>
    <p><input type="text" id="name" name="name" value=""></p>

    <p><label for="password">パスワード</label></p>
    <p><input type="password" id="password" name="password" value=""></p>
    
    <button type="submit" name="operation" value="send">サインイン</button>
</form>

③コントローラーを編集する

UsersController.phpに「signinAction」と「signoutAction」を追加します。

UsersController.phpに以下の赤字部分を追記してください。

<?php
declare(strict_types=1);

class UsersController extends ControllerBase
{
    ~~省略~~

    public function signinAction(){
        if($_POST['operation'] == "send"){
            $user = Users::findFirst([
                'name = :name:',
                'bind' => ['name' => $_POST['name']],
            ]);

            //ユーザー名がDBに無い場合の処理
            if(empty($user)){
                $errorMessage = "名前またはパスワードが間違っています";
                $this->view->errorMessage = $errorMessage;

                //コントローラを通らず「users/signin.phtml」を表示させる
                return $this->view->pick('/users/signin');
            }

            //パスワードが合っているか判定
            if(password_verify($_POST['password'], $user->password)){
                //ログイン成功時の処理
                $signinId = password_hash($user->name, PASSWORD_DEFAULT);

                //クッキーにログインのための情報を格納
                setcookie('signinId', $signinId, time()+60*60, '/', '', false, true);

                //セッションにログインユーザーの情報を格納
                $_SESSION['signinId'] = $signinId;
                $_SESSION['name'] = $user->name;
                $_SESSION['user_id'] = $user->id;

                return $this->response->redirect("users/index");
            }else{
                //ログイン失敗時の処理
                $errorMessage = "名前またはパスワードが間違っています";
                $this->view->errorMessage = $errorMessage;
                return $this->view->pick('/users/signin');
            }
        }
    }

    public function signoutAction(){
        //クッキーの削除
        setcookie('signinId', '', time()-1, '/', '', false, true);

        //セッションの削除
        session_destroy();
        return $this->response->redirect('users/signin');
    }
}

④ControllerBase.phpの編集

/app/controllers/ControllerBase.phpはUsersController.phpの親クラスです。

ControllerBase.phpを活用すると、各コントローラの全てのアクションの実行前に実行させる処理を設定できます。

/app/controllers/ControllerBase.phpを以下のように書き替えてください。

<?php
declare(strict_types=1);

use Phalcon\Mvc\Controller;

class ControllerBase extends Controller
{
    public function initialize(){
        //セッション開始
        session_start();

        //サインイン有無の確認:サインインしてなければサインインページに遷移させる
        if($_SERVER['REQUEST_URI'] != '/users/signin' && $_SERVER['REQUEST_URI'] != '/users/add'){
            if(empty($_SESSION['name'])){
                return $this->response->redirect('users/signin');
            }
        }

        //サインイン済みならサインインページにアクセスさせない
        if($_SERVER['REQUEST_URI'] == '/users/signin'){
            if(!empty($_SESSION['name'])){
                return $this->response->redirect('users/index');
            }
        }
    }
}

⑤ヘッダーにユーザー名を表示させる

④までで認証機能は実装できたので、ヘッダーにサインインしたユーザー名を表示させてみます。

ビューのおおもとのファイルである「/app/views/index.phtml」にヘッダーをつくります。

/app/views/index.phtmlに以下の赤字部分を追記してください。

<!DOCTYPE html>
<html>
    <head>
        ~~省略~~
    </head>
    <body>
        
        <?php if(!empty($_SESSION['name'])): ?>
            <header style="text-align:end;">
                <p>ログインユーザー:<?= $_SESSION['name']; ?></p>
                <p><a href='/users/signout'>ログアウト</a><p>
            </header>
        <?php endif; ?>

        <div class="container">
            <?php echo $this->getContent(); ?>
        </div>

        ~~省略~~
    </body>
</html>

これで認証機能の実装は完了です!!

表示の確認

以下のように表示されていればOKです!

【サインイン画面】URL:localhost:8000/users/signin

【サインイン後に遷移する画面】

おわりに

今回はできるだけシンプルに認証機能を実装するため、デザインや仕様は必要最低限に留めています。

必要に応じてコードを書き換えてお使いください。