WebTecNote

[php] ワンタイムURLの作成と登録確認のメール送信

ユーザー登録の確認で、入力されたメールアドレスに有効期限つきのURLが送信されて
そのリンクをクリックしてページを表示して初めて登録が完了するというのがある。
それについてGoogle先生に聞いても具体的に教えてもらえなかったので、想像だけで組んでみた。

send:

  1. ユーザーから送信された登録情報をチェック
  2. エラーがなければトークン作成
  3. トークンを名前にしたテキストファイルをトークンディレクトリに作成
  4. 作成されたトークンファイルに有効期限のタイムスタンプ保存
  5. トークンを追加したURLを本文に書いたメール送信
  6. 送信しましたメッセージ表示

access:

  1. メール送信したURLにアクセスがある
  2. GETでトークン取得
  3. トークンディレクトリから同名のファイルを検索
  4. ファイル作成日+期限と現在時刻の比較
  5. 期限内であればtrue ファイル削除
  6. 期限外であればfalse ファイル削除

フォルダをテーブルにしてファイルをレコードにすればデータベースでも…

サンプルなので色々省いてます。

$tokendir = dirname( __FILE__ ). DIRECTORY_SEPARATOR ."token" . DIRECTORY_SEPARATOR;
	
if($_POST["mail"]==""){
	print "メールアドレスを入力してください";
}elseif(mb_strlen($_POST["mail"]) > 0 && !preg_match("/^([a-z0-9_]|\-|\.|\+)+@(([a-z0-9_]|\-)+\.)+[a-z]{2,6}$/i",$_POST["mail"])){
	print "メールアドレスの書式に誤りがあります。";
}else{
	print "確認メールを送信しました";
	mail_to_token($_POST["mail"]);
}

if(isset($_GET["key"])){
	if(delete_old_token($_GET["key"]))
		print "登録完了しました。";
	else
		print 'もう一度初めからやり直してください。';
}


function mail_to_token($address)
{
	global $tokendir;
	
	$limit = (time()+3600);
	
	$token= rand(0,100).uniqid();//トークン
	
	touch($tokendir.$token.".log");//トークンファイル作成

	$url = $_SERVER["HTTP_REFERER"]."?key=".$token;
	
	file_put_contents($tokendir.$token.".log", $limit, LOCK_EX);//期限保存
	
	delete_old_token($tokendir);//古いトークン削除
	
	//本文スタイル
	$message="登録を完了するには、以下のアドレスを開いてください。\n60分以内にアクセスが無かった場合は無効となります。\n";
	$message.=$url."\n\n";
	
	my_send_mail($address,'登録確認',$message);
}


function delete_old_token($token = NULL)
{
	global $tokendir;
	
	if (is_dir($tokendir)) {
		if ($dh = opendir($tokendir)) {
			while (($file = readdir($dh)) !== false) {
				if(is_file($tokendir.$file) && is_null($token)){
					$data = file_get_contents($tokendir.$file);
					if(time() > $data) unlink($tokendir.$file);
				}else if(is_file($tokendir.$file) && !is_null($token)){
					if(time() < (filemtime($tokendir.$token.".log")+3600) ){
						@unlink($tokendir.$token.".log");
						return true;
					}else{
						@unlink($tokendir.$token.".log");
						return false;
					}
				}
			}
			closedir($dh);
		}
	}
}

function my_send_mail($mailto, $subject, $message)
{
	
	$message = mb_convert_encoding($message, "JIS", "UTF-8");
	$subject = mb_convert_encoding($subject, "JIS", "UTF-8");
	
	$header ="From: WebTecNote <info@example.com>\n";
	
	mb_send_mail($mailto, $subject, $message, $header);
}

トークン作るときついでに古いトークンも消す。
消す時にファイルに保存されているタイムスタンプを利用する。

アクセスされたトークンは$_GET[“key”]で取得。

多分こんな感じなんじゃないかと思うんだ。

ワンタイムURLクリック後にメール送信処理とか

実際に使う時は管理者にメール送ったり登録者に登録内容送ったりする手順が入ると思う。
トークン作ってメール送るまでは$_POSTの情報使えばいいが、
ワンタイムURLをクリックした後にメールを送信するならメアドを一時的に保存しなければならない。
それならトークン作った時に期限と一緒にメアド保存しておけば後であれこれ処理出来るよね、っていうサンプルです。

  1. ユーザーから送信された登録情報をチェック
  2. エラーがなければトークン作成
  3. トークンを名前にしたテキストファイルをトークンディレクトリに作成
  4. 作成されたトークンファイルに有効期限のタイムスタンプとメールアドレス保存
  5. トークンを追加したURLを本文に書いたメール送信
  6. 送信しましたメッセージ表示
  7. メール送信したURLにユーザーがアクセスする
  8. GETでトークン取得
  9. トークンディレクトリから同名のファイルを検索
  10. ファイル作成日+期限と現在時刻の比較
  11. ファイル内容からメールアドレス取得
  12. 期限内であればtrue ファイル削除
  13. 期限外であればfalse ファイル削除
  14. ユーザーに登録完了メール送信
  15. 管理者に登録完了メール送信
$tokendir = dirname( __FILE__ ). DIRECTORY_SEPARATOR ."token" . DIRECTORY_SEPARATOR;
$email = "";
$success_page ="http://example.com/";
	
if($_POST["mail"]==""){
	print "メールアドレスを入力してください";
}elseif(mb_strlen($_POST["mail"]) > 0 && !preg_match("/^([a-z0-9_]|\-|\.|\+)+@(([a-z0-9_]|\-)+\.)+[a-z]{2,6}$/i",$_POST["mail"])){
	print "メールアドレスの書式に誤りがあります。";
}else{
	print "確認メールを送信しました";
	mail_to_token($_POST["mail"]);
}

if(isset($_GET["key"])){
	if(delete_old_token($_GET["key"])){
		mail_to_master($email);//管理者にメール
		mail_to_success($email);//ユーザーにメール
		print "登録完了メールを送信しました。<a href=\"{$success_page}\">ログインページへ</a>";
	}else{
		print 'もう一度初めからやり直してください。';
	}
}


function mail_to_token($address)
{
	global $tokendir;
	
	$limit = (time()+3600);
	
	$token= rand(0,100).uniqid();//トークン
	
	touch($tokendir.$token.".log");//トークンファイル作成

	$url = $_SERVER["HTTP_REFERER"]."?key=".$token;
	
	file_put_contents($tokendir.$token.".log", $limit, LOCK_EX);//期限保存
	
	delete_old_token($tokendir);//古いトークン削除
	
	$message="登録を完了するには、以下のアドレスを開いてください。\n60分以内にアクセスが無かった場合は無効となります。\n";
	$message.= $url."\n\n";
	
	my_send_mail($address,'登録確認',$message);
}

function mail_to_success($mailto)
{
	
	$message="登録が完了しました。\n\nログインパスワード:password\n\n";
	$message.="こちらでログインできます:".$success_page."\n\n";
	
	my_send_mail($mailto, '['.SITENAME.']登録完了', $message);
}

function mail_to_master($mail)
{
	$message ="新しく登録されたユーザーの情報は次の通りです。\n";
	$message.="────────────────────────────────────\n";
	$message.= $mail."\n\n";
	$message.="DATE: ".date("Y/m/d - H:i:s")."\n";
	$message.="IP: ".$_SERVER['REMOTE_ADDR']."\n";
	$message.="HOST: ".@gethostbyaddr($_SERVER['REMOTE_ADDR'])."\n";
	$message.="USER AGENT: ".$_SERVER['HTTP_USER_AGENT']."\n";
	$message.="────────────────────────────────────\n";
	
	my_send_mail(MAILTO_MASTER, 'ユーザー登録通知', $message);
}

function delete_old_token($token = NULL)
{
	global $tokendir,$email;
	
	if (is_dir($tokendir)) {
		if ($dh = opendir($tokendir)) {
			while (($file = readdir($dh)) !== false) {
				if(is_file($tokendir.$file) && is_null($token)){
					
					$log = file_get_contents($tokendir.$file);
					list($data,$mail) = split("<>",$log);
					$email = $mail;
					if(time() > $data) @unlink($tokendir.$file);
					
				}else if(basename($file,".log")==$token && !is_null($token)){
					
					if(time() < (filemtime($tokendir.$token.".log")+180) ){
					
						$log = file_get_contents($tokendir.$token.".log");
						list($data,$mail) = split("<>",$log);
						$email = $mail;
					
						@unlink($tokendir.$token.".log");
						return true;
					}else{
						@unlink($tokendir.$token.".log");
						return false;
					}
				}
			}
			closedir($dh);
		}
	}
}

function my_send_mail($mailto, $subject, $message)
{
	
	$message = mb_convert_encoding($message, "JIS", "UTF-8");
	$subject = mb_convert_encoding($subject, "JIS", "UTF-8");
	
	$header ="From: WebTecNote <info@example.com>\n";
	
	mb_send_mail($mailto, $subject, $message, $header);
}

JISにしないと文字化けするの忘れてた。

モバイルバージョンを終了