Portfolio作成 ⑧ 管理画面 - ログイン(Session)機能実装編

作業内容
先に完成状態の共有から
ログイン画面(login.php)の作成

管理トップ画面(index.php)へ「ログアウト」リンクを追加

では、作業手順をお伝えします。
認証基盤の作成
ログイン機能実装
login.php(ログイン画面)ファイル へさらに認証処理追記
動作確認(ログイン画面 → ログイン失敗 → ログイン成功 → 管理トップ画面)
auth.php(管理画面保護用)ファイル の作成・各ファイル へ追記
動作確認(ログアウト状態で 記事一覧画面 → リダイレクトされて ログイン画面 → ログイン画面でログイン → 管理トップ画面)
ログアウト機能実装
logout.php(ログアウト画面)ファイル の作成・記述
index.php(管理トップ画面)ファイル へログアウトリンク追加
動作確認(ログイン画面 → 管理トップ画面 → ログアウト → ログイン画面)(ログアウト状態で 記事一覧画面 → リダイレクトされて ログイン画面)
記事編集画面の作成 や 編集(Update)/ 削除(Delete)機能実装 について知りたい方は、こちらの記事をご一読ください。
認証基盤の作成
usersテーブルの作成
まず、MySQL へログインします。

開く際に、ユーザー名とパスワードを求められます。

database.php(データベース接続設定) へ記述した
MySQLユーザー名
MySQLパスワード
を入力してください。
phpMyAdmin へログインできたら、左側のエリアから対象のDBを選択して、「SQL」タブを選択

この部分を確認して、対象のDBが選択されていたらOK

usersテーブル を作成するSQLを投下

CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE users (・・・);
usersテーブル を作成
id INT AUTO_INCREMENT PRIMARY KEY,
ユーザーID(自動採番・主キー)
username VARCHAR(100) NOT NULL UNIQUE,
ユーザー名(最大100文字・必須・重複不可)
password VARCHAR(255) NOT NULL,
パスワード(最大255文字・ハッシュ化して保存する想定・必須)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
登録日時(レコード作成時の日時を自動設定)
「実行」ボタンを押し忘れないようにご注意ください。

左側のエリアに「usersテーブル」が確認できたら、作成完了。

管理者ユーザーの作成
ユーザーを usersテーブル へ登録するにあたり
- ユーザー名
- パスワード
が必要です。
今回は、パスワードをハッシュ化するので一時ファイルとして password-test.php を用意します。
portfolio(個人開発環境)配下へ password-test.php を作成

portfolio/
│
├── index.php ← 記事一覧(フロント)
├── post.php ← 記事詳細(フロント)
├── header.php ← 共通ヘッダー
├── footer.php ← 共通フッター
├── style.css ← CSS
├── test-db.php ← DB接続テスト
├── test-posts.php ← 投稿取得テスト
├── password-test.php ← パスワードのハッシュ化
│
├── config/
│ └── database.php ← PDO接続設定
│
└── admin/
├── index.php ← 管理トップ
├── posts.php ← 記事一覧(管理)
├── new-post.php ← 記事作成
├── edit-post.php ← 記事編集
└── delete-post.php ← 記事削除
作成した password-test.php へ記述していきます。
<?php
echo password_hash('ここにパスワードを入力', PASSWORD_DEFAULT);
https://独自ドメイン/portfolio/password-test.php
へアクセス。
文字の羅列が表示されるのでコピーします。
表示例)$2y$10$AbCdEf........
MySQL へ戻り、usersテーブル へ管理者ユーザーを登録します。

INSERT INTO users (
username,
password
)
VALUES (
'ここにユーザー名',
'ここにコピーしたハッシュ'
);
INSERT INTO users (・・・)
usersテーブル に新しいユーザー情報を登録
username
ユーザー名を登録するカラム
password
パスワード(ハッシュ値)を登録するカラム
VALUES (・・・);
登録する実際の値を()内に記述する
'ここにユーザー名' 'ここにコピーしたハッシュ'
指定したカラムの順番に合わせて値を入力
「実行」ボタンを押す。
なぜハッシュ化するのか
DB(データベース)の情報が流出した際に、
パスワードをそのまま登録していたら即悪用されてしまう。
しかし、DBへの登録上は
$2y$10$AbCdEf........
のようにハッシュ化されていると、元のパスワードが分からず不正アクセスを阻止できる。
login.php(ログイン画面)ファイル の作成・記述
portfolio(個人開発環境)配下の admin(管理者画面)配下に login.php(ログイン画面)ファイルの作成を行います。

portfolio/
│
├── index.php ← 記事一覧(フロント)
├── post.php ← 記事詳細(フロント)
├── header.php ← 共通ヘッダー
├── footer.php ← 共通フッター
├── style.css ← CSS
├── test-db.php ← DB接続テスト
├── test-posts.php ← 投稿取得テスト
├── password-test.php ← パスワードのハッシュ化
│
├── config/
│ └── database.php ← PDO接続設定
│
└── admin/
├── index.php ← 管理トップ
├── posts.php ← 記事一覧(管理)
├── new-post.php ← 記事作成
├── edit-post.php ← 記事編集
├── delete-post.php ← 記事削除
└── login.php ← ログイン
続いて、作成した login.php(ログイン画面)へ記述していきます。
<?php
// 共通ヘッダーを読み込む
// ナビゲーションやHTMLの<head>などを共通化するため
include '../header.php';
?>
<!-- ページ見出し -->
<h2>ログイン</h2>
<!--
ログインフォーム
method="POST"
→ 入力内容をPOST送信する
→ URLにユーザー名やパスワードが表示されない
-->
<form method="POST">
<!-- ユーザー名入力欄のラベル -->
<p>ユーザー名</p>
<!--
ユーザー名入力欄
type="text"
→ 一般的な文字列入力
name="username"
→ サーバー側で $_POST['username'] として取得できる
-->
<input
type="text"
name="username"
>
<!-- パスワード入力欄のラベル -->
<p>パスワード</p>
<!--
パスワード入力欄
type="password"
→ 入力した文字を●や*で隠して表示
name="password"
→ サーバー側で $_POST['password'] として取得できる
-->
<input
type="password"
name="password"
>
<!-- 改行と余白 -->
<br><br>
<!--
送信ボタン
type="submit"
→ フォームの内容を送信する
-->
<button type="submit">
ログイン
</button>
</form>
<?php
// 共通フッターを読み込む
// コピーライトや閉じタグなどを共通化するため
include '../footer.php';
?>
動作確認(ログイン画面)
https://独自ドメイン/portfolio/admin/login.php
ログイン画面が崩れず表示されるかを確認します。

問題なく表示されていることを確認。
ログイン機能実装
login.php(ログイン画面)ファイル へ認証処理追記
login.php(ログイン画面)へ 認証処理 を追記していきます。
追記場所は、ファイル先頭 です。
<?php
session_start();
require_once '../config/database.php';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = trim($_POST['password']);
$stmt = $pdo->prepare("
SELECT *
FROM users
WHERE username = :username
");
$stmt->bindValue(':username', $username);
$stmt->execute();
$user = $stmt->fetch();
if (
$user &&
password_verify($password, $user['password'])
) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
header('Location: index.php');
exit;
} else {
$error = 'ユーザー名またはパスワードが違います';
}
}
?>
include '../header.php';
?>
session_start();
セッションを開始宣言。 ログイン状態を保持するために使用
require_once '../config/database.php';
database.php(データベース接続設定)を読み込む
$error = '';
エラーメッセージを格納するための変数
if ($_SERVER['REQUEST_METHOD'] === 'POST') {・・・}
フォームがPOST送信された場合のみ処理{・・・}を実行
$username = trim($_POST['username']);
前後の空白を削除して「ユーザー名」を取得
$password = trim($_POST['password']);
前後の空白を削除して「パスワード」を取得
$stmt = $pdo->prepare("・・・");
"・・・" に記述したSQLを準備状態で $stmt へ格納
SELECT *
FROM users
WHERE username = :username
ユーザー名 を条件に、usersテーブル から対象のユーザー情報をすべて取得
$stmt->bindValue(':username', $username);
bindValue() は、値の割り当て。$stmt の内容を基に「$username」の値を「:username」に割り当てしています。
$stmt->execute();
$stmt の内容を基に、SQLを実行
$user = $stmt->fetch();
$stmt の内容を基に、検索結果を1件取得。$user へ格納
if (
$user &&
password_verify($password, $user['password'])
) {・・・} else {・・・}
$user が存在し、入力パスワード($password)と 保存済みハッシュ($user['password'])が一致する場合の {・・・} を実行。または、一致していない場合の else {・・・} を実行。
$_SESSION['user_id'] = $user['id'];
ユーザーIDをセッションに保存
$_SESSION['username'] = $user['username'];
ユーザー名をセッションに保存
header('Location: index.php');
index.php(管理画面トップ)へリダイレクト
exit;
PHPの処理を終了する宣言
$error = 'ユーザー名またはパスワードが違います';
ログイン失敗時にエラーメッセージ「ユーザー名またはパスワードが違います」を表示する。
login.php(ログイン画面)ファイル へさらに認証処理追記
login.php(ログイン画面)へ さらに認証処理 を追記していきます。
追記場所は、
<h2>ログイン</h2>
の直後です。
<?php include '../header.php'; ?>
<h2>ログイン</h2>
<?php if ($error): ?>
<p>
<?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?>
</p>
<?php endif; ?>
<form method="POST">
<?php if ($error): ?>
$error に値が入っている場合のみ表示
htmlspecialchars(・・・)
XSS対策。 HTML特殊文字をエスケープ(無害化)する関数。 単なる文字列として表示され、JavaScriptは実行されないようにする。
$error, ENT_QUOTES, 'UTF-8'
$error = 表示するエラーメッセージ ENT_QUOTES = シングルクォート・ダブルクォートを変換 'UTF-8' = 文字コード
<?php endif; ?>
if文終了
動作確認(ログイン画面 → ログイン失敗 → ログイン成功 → 管理トップ画面)
https://独自ドメイン/portfolio/admin/login.php
実際にログイン機能が動作するのかを確認します。
一旦、間違ったユーザー情報を入力してログインしようとした場合を確認します。

きちんとエラー文が表示されて、ログインできないことを確認。
次に、登録しておいた管理者ユーザー情報でログインします。

ログインが成功し、管理トップ画面(index.php)へ遷移することを確認。
auth.php(管理画面保護用)ファイル の作成・各ファイル へ追記
毎ページに同じ認証チェックを記述する手間を省くため、auth.php(管理画面保護用)ファイル を作成を行います。

portfolio/
│
├── index.php ← 記事一覧(フロント)
├── post.php ← 記事詳細(フロント)
├── header.php ← 共通ヘッダー
├── footer.php ← 共通フッター
├── style.css ← CSS
├── test-db.php ← DB接続テスト
├── test-posts.php ← 投稿取得テスト
├── password-test.php ← パスワードのハッシュ化
│
├── config/
│ └── database.php ← PDO接続設定
│
└── admin/
├── index.php ← 管理トップ
├── posts.php ← 記事一覧(管理)
├── new-post.php ← 記事作成
├── edit-post.php ← 記事編集
├── delete-post.php ← 記事削除
├── login.php ← ログイン
└── auth.php ← 管理画面保護用
続いて、作成した auth.php(管理画面保護用)へ記述していきます。
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
session_start();
セッションを開始宣言。ログイン情報を利用するため必須。
if (!isset($_SESSION['user_id'])) {・・・}
user_id がセッションに保存されていない場合、{・・・} を実行。 保存されている場合、実行されない。
header('Location: login.php');
login.php(ログイン画面)へリダイレクト
exit;
PHPの処理を終了する宣言
以下ファイルへ追記していきます。
- index.php
- posts.php
- new-post.php
- edit-post.php
- delete-post.php
追記場所は、ファイル先頭 です。
<?php
require_once 'auth.php';
?>
require_once 'auth.php';
auth.php(管理画面保護用)を読み込む
portfolio/
│
├── index.php ← 記事一覧(フロント)
├── post.php ← 記事詳細(フロント)
├── header.php ← 共通ヘッダー
├── footer.php ← 共通フッター
├── style.css ← CSS
├── test-db.php ← DB接続テスト
├── test-posts.php ← 投稿取得テスト
├── password-test.php ← パスワードのハッシュ化
│
├── config/
│ └── database.php ← PDO接続設定
│
└── admin/
├── index.php ← 管理トップ
├── posts.php ← 記事一覧(管理)
├── new-post.php ← 記事作成
├── edit-post.php ← 記事編集
├── delete-post.php ← 記事削除
├── login.php ← ログイン
└── auth.php ← 管理画面保護用
動作確認(ログアウト状態で 記事一覧画面 → リダイレクトされて ログイン画面 → ログイン画面でログイン → 管理トップ画面)
まだログアウト機能を実装していないため、一度ブラウザを再起動を行い、強制的にログアウト後、動作確認を行ってください。
どうしてもログイン状態が維持される場合は、ログアウト機能実装後に動作確認を行ってください。
ログアウト状態で、各管理画面へアクセスすると、ログイン画面へリダイレクトされるか確認します。
ログアウトします。
https://独自ドメイン/portfolio/admin/posts.php

アクセスすると

ログイン画面(login.php)へリダイレクトされることを確認。
再度、ログインできるかを確認。

問題なくログイン機能に干渉していないことを確認。
ログアウト機能実装
logout.php(ログアウト画面)ファイル の作成・記述
portfolio(個人開発環境)配下の admin(管理者画面)配下に logout.php(ログアウト画面)ファイルの作成を行います。

portfolio/
│
├── index.php ← 記事一覧(フロント)
├── post.php ← 記事詳細(フロント)
├── header.php ← 共通ヘッダー
├── footer.php ← 共通フッター
├── style.css ← CSS
├── test-db.php ← DB接続テスト
├── test-posts.php ← 投稿取得テスト
├── password-test.php ← パスワードのハッシュ化
│
├── config/
│ └── database.php ← PDO接続設定
│
└── admin/
└── index.php ← 管理トップ
├── posts.php ← 記事一覧(管理)
├── new-post.php ← 記事作成
├── edit-post.php ← 記事編集
├── delete-post.php ← 記事削除
├── login.php ← ログイン
├── logout.php ← ログアウト
└── auth.php ← 管理画面保護用
続いて、作成した logout.php(ログアウト画面)へ記述していきます。
<?php
session_start();
/*
|--------------------------------------------------------------------------
| セッション削除
|--------------------------------------------------------------------------
*/
$_SESSION = [];
session_destroy();
/*
|--------------------------------------------------------------------------
| ログイン画面へ
|--------------------------------------------------------------------------
*/
header('Location: login.php');
exit;
session_start();
セッションを開始宣言。ログイン情報を利用するため必須。
$_SESSION = [];
セッション変数をすべて空にする
session_destroy();
セッション自体を破棄する。 サーバー側のセッション情報を削除
header('Location: login.php');
login.php(ログイン画面)へリダイレクト
exit;
PHPの処理を終了する宣言
index.php(管理トップ画面)ファイル へログアウトリンク追加
index.php(管理トップ画面)へログアウトリンクを追記していきます。
この部分へ
<ul>
<li><a href="new-post.php">新規投稿</a></li>
<li><a href="posts.php">記事一覧</a></li>
</ul>
追記ログアウトリンクを追記していきます。
<ul>
<li><a href="new-post.php">新規投稿</a></li>
<li><a href="posts.php">記事一覧</a></li>
<li><a href="logout.php">ログアウト</a></li>
</ul>
href="logout.php"
logout.php(ログアウト画面)へのリンクです。
動作確認(ログイン画面 → 管理トップ画面 → ログアウト → ログイン画面)(ログアウト状態で 記事一覧画面 → リダイレクトされて ログイン画面)
https://独自ドメイン/portfolio/admin/login.php
ログインを行い、index.php(管理トップ画面)に設置されている「ログアウト」ボタン でログアウトが行えるかを確認します。
「ユーザー名」「パスワード」を入力し、「ログイン」ボタンを押します。
問題なく、index.php(管理トップ画面)へログイン成功。

「ログアウト」ボタンが表示されていることを確認。
そのまま「ログアウト」ボタンを押します。

ログアウトされ、login.php(ログイン画面)へリダイレクトされました。
https://独自ドメイン/portfolio/admin/posts.php

アクセスすると

login.php(ログイン画面)へリダイレクトされました。
実装した内容すべてが問題なく動作していることを確認できました。
まとめ
今回は、ログイン / ログアウト(Session)機能の実装 について書かせていただきました。
現在、 portfolio へのアクセスは Basic認証 を設定していますが、CSRF(Cross-Site Request Forgery)などの対策設定ができたら表の画面は公開してもよいかもしれないと思いました。
ということで、次回記事は「CSRF」 や 「入力バリデーション」 を実装していこうと思います。
ご興味ありましたら、次回の記事も是非ご一読ください。
その他、WordPress自作開発についての記事を確認したい方は、こちらのカテゴリーを覗いていただければと思います。
カテゴリー「WordPress自作開発」
ここまで読んでいただき、ありがとうございました。
