メール認証を利用した新規会員登録機能を作成します。今回は最後の3回目で、前回の記事に引き継ぎ会員の本登録部分を実装していきます。
前回記事
スポンサーリンク
最終回は上記のイラストにおいて、手順3・4の会員本登録機能を実装していきます。
3 会員登録フォーム(registration_form.php)
会員登録確認(registration_check.php)
4 会員登録完了(registration_insert.php)
まず、本登録用のmemberテーブルを準備します。
memberテーブル
1 2 3 4 5 6 7 |
CREATE TABLE member ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, account VARCHAR(50) NOT NULL, mail VARCHAR(50) NOT NULL, password VARCHAR(128) NOT NULL, flag TINYINT(1) NOT NULL DEFAULT 1 )ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; |
アカウント・メールアドレス・パスワード(ハッシュ化したパスワード)を保存します。flagに関しては今回は特に利用しませんが、デフォルトで1が自動入力されます。
registration_form.php(会員登録フォーム)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
<?php session_start(); header("Content-type: text/html; charset=utf-8"); //クロスサイトリクエストフォージェリ(CSRF)対策 $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32)); $token = $_SESSION['token']; //クリックジャッキング対策 header('X-FRAME-OPTIONS: SAMEORIGIN'); //データベース接続 require_once("db.php"); $dbh = db_connect(); //エラーメッセージの初期化 $errors = array(); if(empty($_GET)) { header("Location: registration_mail_form.php"); exit(); }else{ //GETデータを変数に入れる $urltoken = isset($_GET[urltoken]) ? $_GET[urltoken] : NULL; //メール入力判定 if ($urltoken == ''){ $errors['urltoken'] = "もう一度登録をやりなおして下さい。"; }else{ try{ //例外処理を投げる(スロー)ようにする $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //flagが0の未登録者・仮登録日から24時間以内 $statement = $dbh->prepare("SELECT mail FROM pre_member WHERE urltoken=(:urltoken) AND flag =0 AND date > now() - interval 24 hour"); $statement->bindValue(':urltoken', $urltoken, PDO::PARAM_STR); $statement->execute(); //レコード件数取得 $row_count = $statement->rowCount(); //24時間以内に仮登録され、本登録されていないトークンの場合 if( $row_count ==1){ $mail_array = $statement->fetch(); $mail = $mail_array[mail]; $_SESSION['mail'] = $mail; }else{ $errors['urltoken_timeover'] = "このURLはご利用できません。有効期限が過ぎた等の問題があります。もう一度登録をやりなおして下さい。"; } //データベース接続切断 $dbh = null; }catch (PDOException $e){ print('Error:'.$e->getMessage()); die(); } } } ?> <!DOCTYPE html> <html> <head> <title>会員登録画面</title> <meta charset="utf-8"> </head> <body> <h1>会員登録画面</h1> <?php if (count($errors) === 0): ?> <form action="registration_check.php" method="post"> <p>メールアドレス:<?=htmlspecialchars($mail, ENT_QUOTES, 'UTF-8')?></p> <p>アカウント名:<input type="text" name="account"></p> <p>パスワード:<input type="text" name="password"></p> <input type="hidden" name="token" value="<?=$token?>"> <input type="submit" value="確認する"> </form> <?php elseif(count($errors) > 0): ?> <?php foreach($errors as $value){ echo "<p>".$value."</p>"; } ?> <?php endif; ?> </body> </html> |
7・8行目
クロスサイトリクエストフォージェリ(CSRF)については以下の関連ページをご参照下さい。
11行目
クリックジャッキング対策については以下の関連ページをご参照下さい。
14・15行目
データベースの接続を行っています。db.phpは本ページの下記に記載しています。
35行目
GETメソッドで取得したトークン(urltoken)を元に仮登録(pre_memberテーブル)のデータを検索しています。その際に、仮登録が24時間以内でありflagが0(まだ本登録していない)の条件を指定しています。
registration_check.php(会員登録確認)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
<?php session_start(); header("Content-type: text/html; charset=utf-8"); //クロスサイトリクエストフォージェリ(CSRF)対策のトークン判定 if ($_POST['token'] != $_SESSION['token']){ echo "不正アクセスの可能性あり"; exit(); } //クリックジャッキング対策 header('X-FRAME-OPTIONS: SAMEORIGIN'); //前後にある半角全角スペースを削除する関数 function spaceTrim ($str) { // 行頭 $str = preg_replace('/^[ ]+/u', '', $str); // 末尾 $str = preg_replace('/[ ]+$/u', '', $str); return $str; } //エラーメッセージの初期化 $errors = array(); if(empty($_POST)) { header("Location: registration_mail_form.php"); exit(); }else{ //POSTされたデータを各変数に入れる $account = isset($_POST['account']) ? $_POST['account'] : NULL; $password = isset($_POST['password']) ? $_POST['password'] : NULL; //前後にある半角全角スペースを削除 $account = spaceTrim($account); $password = spaceTrim($password); //アカウント入力判定 if ($account == ''): $errors['account'] = "アカウントが入力されていません。"; elseif(mb_strlen($account)>10): $errors['account_length'] = "アカウントは10文字以内で入力して下さい。"; endif; //パスワード入力判定 if ($password == ''): $errors['password'] = "パスワードが入力されていません。"; elseif(!preg_match('/^[0-9a-zA-Z]{5,30}$/', $_POST["password"])): $errors['password_length'] = "パスワードは半角英数字の5文字以上30文字以下で入力して下さい。"; else: $password_hide = str_repeat('*', strlen($password)); endif; } //エラーが無ければセッションに登録 if(count($errors) === 0){ $_SESSION['account'] = $account; $_SESSION['password'] = $password; } ?> <!DOCTYPE html> <html> <head> <title>会員登録確認画面</title> <meta charset="utf-8"> </head> <body> <h1>会員登録確認画面</h1> <?php if (count($errors) === 0): ?> <form action="registration_insert.php" method="post"> <p>メールアドレス:<?=htmlspecialchars($_SESSION['mail'], ENT_QUOTES)?></p> <p>アカウント名:<?=htmlspecialchars($account, ENT_QUOTES)?></p> <p>パスワード:<?=$password_hide?></p> <input type="button" value="戻る" onClick="history.back()"> <input type="hidden" name="token" value="<?=$_POST['token']?>"> <input type="submit" value="登録する"> </form> <?php elseif(count($errors) > 0): ?> <?php foreach($errors as $value){ echo "<p>".$value."</p>"; } ?> <input type="button" value="戻る" onClick="history.back()"> <?php endif; ?> </body> </html> |
※今回は、同じアカウント名でも登録できるようになっています。もし同じアカウント名をはじく場合は、アカウント入力判定の部分でmemberテーブルを検索する必要があります。
registration_insert.php(会員登録完了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
<?php session_start(); header("Content-type: text/html; charset=utf-8"); //クロスサイトリクエストフォージェリ(CSRF)対策のトークン判定 if ($_POST['token'] != $_SESSION['token']){ echo "不正アクセスの可能性あり"; exit(); } //クリックジャッキング対策 header('X-FRAME-OPTIONS: SAMEORIGIN'); //データベース接続 require_once("db.php"); $dbh = db_connect(); //エラーメッセージの初期化 $errors = array(); if(empty($_POST)) { header("Location: registration_mail_form.php"); exit(); } $mail = $_SESSION['mail']; $account = $_SESSION['account']; //パスワードのハッシュ化 $password_hash = password_hash($_SESSION['password'], PASSWORD_DEFAULT); //ここでデータベースに登録する try{ //例外処理を投げる(スロー)ようにする $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //トランザクション開始 $dbh->beginTransaction(); //memberテーブルに本登録する $statement = $dbh->prepare("INSERT INTO member (account,mail,password) VALUES (:account,:mail,:password_hash)"); //プレースホルダへ実際の値を設定する $statement->bindValue(':account', $account, PDO::PARAM_STR); $statement->bindValue(':mail', $mail, PDO::PARAM_STR); $statement->bindValue(':password_hash', $password_hash, PDO::PARAM_STR); $statement->execute(); //pre_memberのflagを1にする $statement = $dbh->prepare("UPDATE pre_member SET flag=1 WHERE mail=(:mail)"); //プレースホルダへ実際の値を設定する $statement->bindValue(':mail', $mail, PDO::PARAM_STR); $statement->execute(); // トランザクション完了(コミット) $dbh->commit(); //データベース接続切断 $dbh = null; //セッション変数を全て解除 $_SESSION = array(); //セッションクッキーの削除・sessionidとの関係を探れ。つまりはじめのsesssionidを名前でやる if (isset($_COOKIE["PHPSESSID"])) { setcookie("PHPSESSID", '', time() - 1800, '/'); } //セッションを破棄する session_destroy(); /* 登録完了のメールを送信 */ }catch (PDOException $e){ //トランザクション取り消し(ロールバック) $dbh->rollBack(); $errors['error'] = "もう一度やりなおして下さい。"; print('Error:'.$e->getMessage()); } ?> <!DOCTYPE html> <html> <head> <title>会員登録完了画面</title> <meta charset="utf-8"> </head> <body> <?php if (count($errors) === 0): ?> <h1>会員登録完了画面</h1> <p>登録完了いたしました。ログイン画面からどうぞ。</p> <p><a href="">ログイン画面(未リンク)</a></p> <?php elseif(count($errors) > 0): ?> <?php foreach($errors as $value){ echo "<p>".$value."</p>"; } ?> <?php endif; ?> </body> </html> |
31行目
パスワードをハッシュ化してデータベースに保存します。ハッシュ化については以下の関連ページをご参照下さい。
ハッシュ関数について/password_hash()を利用する
34〜81行目
会員データを本登録であるmemberテーブルに入力し、それと同時に仮登録であるpre_memberテーブルのflagを1にしています。この二つの操作はトランザクションによって制御されています。トランザクションについては以下の関連ページをご参加下さい。
PDOでトランザクション処理を行う[beginTransaction]
pre_memberテーブルでflagを1にする。
memberテーブル
73行目
本登録が完了したら、完了通知のメールをユーザに送りますが、今回は実装していません。
db.php(データベース接続)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php function db_connect(){ $dsn = 'mysql:host=◯◯◯;dbname=◯◯◯;charset=utf8'; $user = '◯◯◯'; $password = '◯◯◯'; try{ $dbh = new PDO($dsn, $user, $password); return $dbh; }catch (PDOException $e){ print('Error:'.$e->getMessage()); die(); } } |
4〜6行目
環境に合わせて適当な値を設定して下さい。