管理画面にフォーム型ログイン認証を実装する

ここまでで、管理画面用のHTMLをひととおり作成しました。
機能の実装をはじめる前に、管理画面のログイン認証機能を作成したいと思います。

ログイン認証の仕組み

セッションの仕組みを使って、以下のような設計でログイン状況を管理します。
なお、管理画面ユーザーのアカウント情報については、usersテーブルで管理します。

  1. 未ログイン状態で管理画面にアクセスされた場合、ログインフォームに転送
  2. ログインフォームにてログイン情報入力
  3. ログイン情報が正しかった場合、sessionにログイン情報を保存
  4. 以降、全ページでログイン状況をチェック

セッションについて詳しくは、以下を参考にしてください。

PHP: セッション関連 – Manual

PHP: 基本的な使用法 – Manual

テーブルの用意

以下のデータベーステーブルを作成します。

usersテーブルの設計

phpMyAdminから、管理画面用のアカウントを管理するusersテーブルを作成します。
phpMyAdminを開いたら、データベースのトップ画面へ移動します。
「テーブルを作成」ブロックで、「名前:users」「カラム数:5」を指定して、「実行」をクリックしてください。

テーブルの内容は以下となります。

名前 データ型 長さ/値 インデックス A_I コメント
user_id INT PRIMARY ON 通しID
user_loginid VARCHAR 20 ログインID
user_password VARCHAR 100 パスワード
user_updated TIMESTAMP 更新日時
user_created TIMESTAMP 作成日時

SQLは以下となります。

CREATE TABLE `users` (
  `user_id` int(11) NOT NULL COMMENT '通しID',
  `user_loginid` varchar(20) NOT NULL COMMENT 'ログインID',
  `user_password` varchar(100) NOT NULL COMMENT 'パスワード',
  `user_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時',
  `user_created` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '作成日時'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='管理画面ユーザー';
ALTER TABLE `users`
  ADD PRIMARY KEY (`user_id`);
ALTER TABLE `users`
  MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '通しID', AUTO_INCREMENT=1;

アカウントの追加

管理画面用アカウントについては、phpMyAdminから直接作成することにします。
さっそくusersテーブルを開いた画面で、タブの「挿入」をクリックしてください。
レコードを作成する画面が表示されます。

ここでは、以下のように入力します。

user_id: 空欄のままとします。(オートインクリメントなので)
user_loginid: 今回は「admin」とします。
user_password: 今回は「admin_password」とします。また、関数に「SHA1」を指定します。
user_update: 「CURRENT_TIMESTAMP」のままとします。
user_created: 任意の値を入力します。

※phpMyAdminでは、レコード挿入時に関数を指定できます。今回user_passwordについては、セキュリティのため、生のパスワードではなく、SHA1アルゴリズムでハッシュ化したパスワードを保存するようにします。ハッシュ化をしておくことで、万一user_passwordのデータが漏洩しても、被害を最小限にできます。

上記のレコード作成については、以下のSQLを実行することでも実施できます。

INSERT INTO `users` (`user_id`, `user_loginid`, `user_password`, `user_updated`, `user_created`) VALUES
(1, 'admin', SHA1('admin_password'), '2017-01-01 00:00:00', '2017-01-01 00:00:00');

プログラムの修正

ログインフォームの実装のために、以下の修正を行います。

管理画面用の共通処理プログラム

system/common_admin.phpを以下のように修正します。

system/common_admin.php
<?php
// 関連ファイルをロード
require_once "common.php";

// セッションの開始
session_start();

// ログイン状態のチェック
$logined_flag = false; // ログイン状態フラグ
if (!isset($ignore_login)) {
    // 認証用変数をsessionから取得
    if (isset($_SESSION["user_id"])) {
        $session_user_id = $_SESSION["user_id"];
        try {
            $stmt = $db->prepare("SELECT * FROM users WHERE user_id = ? LIMIT 1");
            $stmt->execute(array($session_user_id)); // クエリの実行
            $row_user = $stmt->fetch(PDO::FETCH_ASSOC); // SELECT結果を配列に格納
            if ($row_user) {
                // 該当のuserレコードがあったらログイン状態にする
                $logined_flag = true;
            }
        } catch (PDOException $e) {
            // エラー発生時
            exit("ログイン状態のチェックに失敗しました");
        }
    }
    
    // ログイン状態でなかったら、ログインページへジャンプ
    if (!$logined_flag) {
        header("location: login.php");
        exit;
    }
}

以下でブロックごとに解説します。

// セッションの開始
session_start();

セッションを有効化します。
Webアプリケーションではセッションを利用することで、ページをまたいだ状態の保持を行うことができます。

// ログイン状態のチェック
$logined_flag = false; // ログイン状態フラグ
if (!isset($ignore_login)) {

$logined_flagをfalseで初期化しています。
また、$ignore_loginがfalseの際にログイン状態のチェックを行うように切り分けています。
(login.phpのみ、未ログイン状態でも表示できるようにしています)

    // 認証用変数をsessionから取得
    if (isset($_SESSION["user_id"])) {
        $session_user_id = $_SESSION["user_id"];

スーパーグローバル変数(プログラム上で自動的に初期化される変数)である$_SESSIONから、user_idを取得しています。(login.phpにて、ログイン成功時にuser_idをセットしています)

        try {

try構文を使って、例外(エラー)をcatchできるようにしています。

            $stmt = $db->prepare("SELECT * FROM users WHERE user_id = ? LIMIT 1");
            $stmt->execute(array($session_user_id)); // クエリの実行
            $row_user = $stmt->fetch(PDO::FETCH_ASSOC); // SELECT結果を配列に格納

SQL内のWHERE句にプレースホルダー文字列(?)を記入し、execute時にセッションから取得したlogin_idを指定しています。
その後、該当行をfetchで取得しています。

            if ($row_user) {
                // 該当のuserレコードがあったらログイン状態にする
                $logined_flag = true;
            }

ログイン状態だと判断できたら、ログイン状態を表すフラグにtrueを代入します。

    // ログイン状態でなかったら、ログインページへジャンプ
    if (!$logined_flag) {
        header("Location: login.php");
        exit;
    }

未ログインと判断された場合、強制的にlogin.phpを表示します。
header関数について詳しくは、以下をご覧ください。

PHP: header – Manual

フォーム認証プログラム

admin/login.phpを以下のように修正します。

admin/login.php
<?php $ignore_login = true;?>
<?php require_once "../system/common_admin.php";?>
<?php
// ホワイトリスト変数の作成
$whitelist = array("send", "user_loginid", "user_password");
$request = whitelist($whitelist);

$page_message = ""; // ページに表示するメッセージ
$page_error = ""; // エラーメッセージ

// エラーチェック
if (isset($request["send"])) {
    if ($request["user_loginid"] == "") {
        $page_error .= "ログインIDを入力してください\n";
    }
    if ($request["user_password"] == "") {
        $page_error .= "パスワードを入力してください\n";
    }
}

// ログイン実行
if (isset($request["send"]) && $page_error == "") {
    try {
        // まずはログインIDでSELECTする
        $stmt = $db->prepare("SELECT * FROM users WHERE user_loginid = ? LIMIT 1");
        $stmt->execute(array($request["user_loginid"])); // クエリの実行
        $row_user = $stmt->fetch(PDO::FETCH_ASSOC); // SELECT結果を配列に格納
        if ($row_user) {
            // 該当のuserレコードがあったら、パスワードを照合する
            if (sha1($request["user_password"]) == $row_user["user_password"]) {
                $_SESSION["user_id"] = $row_user["user_id"];
                header("Location: index.php");
                exit;
            }
        }
        $page_error .= "入力内容をご確認ください\n";
    } catch (PDOException $e) {
        // エラー発生時
        exit("ログイン処理に失敗しました");
    }
}
?>
<?php $page_title = "ログイン";?>
<?php require "header.php";?>
    <p class="attention">
      <?php echo nl2br(he($page_error)); ?>
    </p>
    <form action="login.php" method="post">
      <div>
        ログインID<br>
        <input type="text" name="user_loginid" size="30" value="">
      </div>
      <div>
        パスワード<br>
        <input type="password" name="user_password" size="30" value="">
      </div>
      <div>
        <input type="submit" name="send" value="ログインする">
      </div>
    </form>
<?php require "footer.php";?>

以下でブロックごとに解説します。

<?php $ignore_login = true;?>

ログインページのみ、認証チェックを無効化するフラグ変数にtrueを代入します。

// ホワイトリスト変数の作成
$whitelist = array("send", "user_loginid", "user_password");
$request = whitelist($whitelist);

このプログラムで使用する変数を指定し、$requestに代入します。

// エラーチェック
if (isset($request["send"])) {
    if ($request["user_loginid"] == "") {
        $page_error .= "ログインIDを入力してください\n";
    }
    if ($request["user_password"] == "") {
        $page_error .= "パスワードを入力してください\n";
    }
}

必須入力チェックをします。

        // まずはログインIDでSELECTする
        $stmt = $db->prepare("SELECT * FROM users WHERE user_loginid = ? LIMIT 1");
        $stmt->execute(array($request["user_loginid"])); // クエリの実行
        $row_user = $stmt->fetch(PDO::FETCH_ASSOC); // SELECT結果を配列に格納

入力されたログインIDを使用して、アカウントデータを取得します。

        if ($row_user) {
            // 該当のuserレコードがあったら、パスワードを照合する
            if (sha1($request["user_password"]) == $row_user["user_password"]) {
                $_SESSION["user_id"] = $row_user["user_id"];
                header("Location: index.php");
                exit;
            }
        }

パスワードをsha1関数を使って評価します。
正しいパスワードだった場合は、$_SESSION[“user_id”]に値をセットして、管理画面トップページにリダイレクトします。

以上で管理画面のフォーム認証が実装できました。
今回のコンテンツについては以下よりダウンロードできます。

step3-030.zip

このページをシェア Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn