Webアプリケーション開発の核心とも言える「ログイン認証機能」。これがなければ、会員専用ページやユーザーごとのデータ管理は実現できません。
この記事では、これまでの学習の集大成として、PHPで安全かつ実践的なログイン認証システムをゼロから構築する全手順を解説します。ロジックと**ビュー(表示)**を分離した、メンテナンス性の高いファイル構成で、本番環境でも通用する知識を身につけましょう。
1. 準備編:設計とファイル構成
1.1. データベース設計
まず、ユーザー情報を格納するusers
テーブルをphpMyAdminで作成します。
【users
テーブルの構造】 | カラム名 | データ型 | その他 | | :— | :— | :— | | id
| INT | AUTO_INCREMENT, PRIMARY KEY | | name
| VARCHAR(50) | | | email
| VARCHAR(100) | UNIQUE KEY(重複禁止)| | password
| VARCHAR(255) | パスワードハッシュを保存 | | created_at
| DATETIME | |
重要: パスワードは絶対にそのまま保存してはいけません。password_hash()
関数でハッシュ化(元に戻せない暗号化)した文字列を保存するため、VARCHAR(255)
と長めに設定します。
1.2. ファイル構成
コードを役割ごとに整理するため、以下のようなファイル構成で開発を進めます。
/login_system/
├── helpers/
│ ├── db_helper.php # データベース関連の関数
│ └── extra_helper.php # その他の便利関数
├── views/
│ ├── signup_view.php # 新規登録フォームのHTML
│ ├── login_view.php # ログインフォームのHTML
│ └── member_view.php # 会員専用ページのHTML
├── config.php # 設定ファイル
├── signup.php # 新規登録の処理(コントローラー)
├── login.php # ログインの処理(コントローラー)
├── member.php # 会員専用ページの処理(コントローラー)
└── logout.php # ログアウトの処理
2. 基盤編:設定ファイルと共通関数
2.1. config.php
データベース接続情報やサイトのURLなど、変更される可能性のある設定値を定数として定義します。
<?php // config.php
define('DSN', 'mysql:dbname=php_learning_db;host=localhost;charset=utf8mb4');
define('DB_USER', 'root');
define('DB_PASSWORD', '');
define('SITE_URL', 'http://localhost/login_system/');
// エラーをすべて表示する設定(開発時)
error_reporting(E_ALL);
// セッションクッキーの有効期間を24分(1440秒)に設定
session_set_cookie_params(1440, '/');
2.2. ヘルパー関数
helpers/
フォルダに、アプリケーション全体で使う便利関数をまとめます。
【db_helper.php
】 – データベース関連
<?php // helpers/db_helper.php
// (get_db_connection(), email_exists(), insert_user_data(), select_user(), select_all_users() をここに記述)
【extra_helper.php
】 – その他
<?php // helpers/extra_helper.php
// (h(), get_post(), check_words() などをここに記述)
(各関数の詳細なコードは、以降のセクションで作成していきます)
3. 会員登録機能 (signup.php
)
3.1. ヘルパー関数の作成
まず、helpers/db_helper.php
に、メールアドレスの重複チェックと、新規ユーザー登録のための関数を追加します。
<?php // helpers/db_helper.php
function email_exists(PDO $dbh, string $email): bool {
$sql = "SELECT COUNT(id) FROM users WHERE email = :email";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->execute();
$count = $stmt->fetch(PDO::FETCH_COLUMN);
return $count > 0;
}
function insert_user_data(PDO $dbh, string $name, string $email, string $password): bool {
// パスワードをハッシュ化
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (name, email, password, created_at) VALUES (:name, :email, :password, NOW())";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->bindValue(':password', $hashed_password, PDO::PARAM_STR);
return $stmt->execute();
}
// ... 他の関数 ...
3.2. ビューの作成 (signup_view.php
)
ユーザーが情報を入力するフォームです。
<!DOCTYPE html>
<html>
<head><title>新規ユーザー登録</title></head>
<body>
<h1>新規ユーザー登録</h1>
<form action="signup.php" method="POST">
<p>お名前:<input type="text" name="name" value="<?php echo h($name); ?>"></p>
<p>メールアドレス:<input type="email" name="email" value="<?php echo h($email); ?>"></p>
<p>パスワード:<input type="password" name="password"></p>
<p><input type="submit" value="登録する"></p>
</form>
<p><a href="login.php">ログインはこちら</a></p>
</body>
</html>
3.3. コントローラーの作成 (signup.php
)
バリデーションとデータベース挿入のロジックを担当します。
<?php // signup.php
require_once 'config.php';
require_once 'helpers/db_helper.php';
require_once 'helpers/extra_helper.php';
$name = ''; $email = ''; $password = '';
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = get_post('name');
$email = get_post('email');
$password = get_post('password');
$dbh = get_db_connection();
// バリデーション
if (!check_words($name, 50)) { $errors['name'] = 'お名前を50文字以内で入力してください。'; }
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors['email'] = 'メールアドレスの形式が正しくありません。'; }
elseif (email_exists($dbh, $email)) { $errors['email'] = 'このメールアドレスは既に登録されています。'; }
if (!check_words($password, 50, 8)) { $errors['password'] = 'パスワードを8文字以上50文字以内で入力してください。'; }
// エラーがなければ登録処理
if (empty($errors)) {
if (insert_user_data($dbh, $name, $email, $password)) {
header('Location: ' . SITE_URL . 'login.php');
exit;
}
$errors['db'] = '登録に失敗しました。';
}
}
include_once 'views/signup_view.php';
4. ログイン・ログアウト機能
4.1. ヘルパー関数の作成
helpers/db_helper.php
に、Emailでユーザーを検索し、パスワードを検証する関数を追加します。
<?php // helpers/db_helper.php
function select_user(PDO $dbh, string $email, string $password) {
$sql = "SELECT * FROM users WHERE email = :email LIMIT 1";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->execute();
if ($stmt->rowCount() > 0) {
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// パスワードを検証
if (password_verify($password, $user['password'])) {
return $user;
}
}
return false;
}
// ... 他の関数 ...
4.2. コントローラーの作成 (login.php
)
認証処理とセッション管理を行います。
<?php // login.php
require_once 'config.php';
require_once 'helpers/db_helper.php';
require_once 'helpers/extra_helper.php';
session_start();
// ログイン済みの場合は会員ページへリダイレクト
if (!empty($_SESSION['member'])) {
header('Location: ' . SITE_URL . 'member.php');
exit;
}
$email = ''; $password = '';
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = get_post('email');
$password = get_post('password');
$dbh = get_db_connection();
// バリデーション
if (!($member = select_user($dbh, $email, $password))) {
$errors['login'] = 'メールアドレスまたはパスワードが正しくありません。';
}
// ログイン成功
if (empty($errors)) {
// セッションIDを再生成(セッション固定化攻撃対策)
session_regenerate_id(true);
$_SESSION['member'] = $member;
header('Location: ' . SITE_URL . 'member.php');
exit;
}
}
include_once 'views/login_view.php';
重要: session_regenerate_id(true)
は、ログイン成功時にセッションIDを新しく発行しなおすための重要なセキュリティ対策です。
4.3. ログアウト処理 (logout.php
)
セッションを破棄してログインページに戻します。
<?php // logout.php
session_start();
$_SESSION = [];
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 3600, '/');
}
session_destroy();
header('Location: login.php');
exit;
5. 会員専用ページ (member.php
)
ログインしているユーザーだけがアクセスできるページです。
<?php // member.php
require_once 'config.php';
require_once 'helpers/db_helper.php';
require_once 'helpers/extra_helper.php';
session_start();
// 未ログインの場合はログインページへリダイレクト
if (empty($_SESSION['member'])) {
header('Location: ' . SITE_URL . 'login.php');
exit;
}
$member = $_SESSION['member'];
$dbh = get_db_connection();
// (ここで必要なら$dbhを使って他のデータを取得する)
include_once 'views/member_view.php';
【views/member_view.php
】
<!DOCTYPE html>
<html>
<head><title>会員専用ページ</title></head>
<body>
<h1>ようこそ、<?php echo h($member['name']); ?>さん</h1>
<p>ここはログインしたユーザーだけが見られるページです。</p>
<p><a href="logout.php">ログアウト</a></p>
</body>
</html>
ページの冒頭でセッションをチェックし、$_SESSION['member']
が空ならログインページに強制的に戻すのが「認証ガード」の基本です。
まとめ
お疲れ様でした! これで、現代的なセキュリティ対策を施した、本格的なログイン認証システムが完成しました。
【このプロジェクトの最重要ポイント】
- パスワードは
password_hash()
でハッシュ化し、password_verify()
で検証する。 - ログイン成功時には**
session_regenerate_id(true)
**でセッションIDを更新し、セキュリティを高める。 - ロジックとビューをファイルを分離して管理することで、コードの見通しとメンテナンス性を向上させる。
- 会員専用ページでは、必ず冒頭でログイン状態をチェックする。
このアプリケーションの構造は、あらゆるWebサービスの基本となります。ぜひこのコードを基盤として、機能の追加や改修に挑戦してみてください。