Last active
May 21, 2018 03:30
-
-
Save Go-Noji/096bca1c663b1fc5e6fad9ea1873bf91 to your computer and use it in GitHub Desktop.
CodeIgniterで時間制限付きメール認証機能を作る ref: https://qiita.com/Go-Noji/items/6a8e09e66b3f4857266e
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//~略 | |
$config['salt'] = 'h6F4htejW69moG6gndjplC12'; | |
$config['auth_time'] = 3600; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE TABLE `mailauth`.`user` | |
( | |
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'AI' , | |
`mail` VARCHAR(255) NOT NULL COMMENT 'メールアドレス' , | |
`password` VARCHAR(64) NOT NULL COMMENT 'パスワード(sha256)' , | |
`name` VARCHAR(255) NOT NULL COMMENT 'ユーザー名' , | |
`status` INT(1) NOT NULL COMMENT '0:仮登録, 1は通常' , | |
PRIMARY KEY (`id`) | |
) ENGINE = InnoDB CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT = 'ユーザー'; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//このように編集しておくと複数の状況で各設定を使いまわせます | |
$mail = array( | |
'field' => 'mail', | |
'label' => 'メールアドレス', | |
'rules' => 'required|valid_email|is_unique[user.mail]', | |
'errors' => array( | |
'required' => '{field} は必須です', | |
'valid_email' => '{field} がメールアドレスとして正しくありません', | |
'is_unique' => '{field} は既に存在しています' | |
) | |
); | |
$password = array( | |
'field' => 'password', | |
'label' => 'パスワード', | |
'rules' => array( | |
'required', | |
array( | |
'isPassword', | |
function ($password) | |
{ | |
if($password === '') | |
{ | |
return TRUE; | |
} | |
if(preg_match('/\A(?=.*?[a-z])(?=.*?\d)[!-~]{8,100}+\z/i', $password)) | |
{ | |
return TRUE; | |
} | |
return FALSE; | |
} | |
) | |
), | |
'errors' => array( | |
'required' => '{field} は必須です', | |
'isPassword' => '{field} は8文字以上100文字以下の英数字記号で入力してください' | |
) | |
); | |
$passconf = array( | |
'field' => 'passconf', | |
'label' => 'パスワード(再入力)', | |
'rules' => 'matches[password]', | |
'errors' => array( | |
'matches' => '{field} が一致しません' | |
) | |
); | |
$name = array( | |
'field' => 'name', | |
'label' => 'ユーザーネーム', | |
'rules' => 'required|max_length[128]', | |
'errors' => array( | |
'required' => '{field} は必須です', | |
'max_length' => '{field} は128文字以下で入力してください' | |
) | |
); | |
//この名前で設定しておけばRegisterクラスのindexメソッドで勝手に呼び出されるようになります | |
$config['register/index'] = array($mail, $password, $passconf, $name); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//このように編集しておくと複数の状況で各設定を使いまわせます | |
$mail = array( | |
'field' => 'mail', | |
'label' => 'メールアドレス', | |
'rules' => 'required|valid_email|is_unique[user.mail]', | |
'errors' => array( | |
'required' => '{field} は必須です', | |
'valid_email' => '{field} がメールアドレスとして正しくありません', | |
'is_unique' => '{field} は既に存在しています' | |
) | |
); | |
$password = array( | |
'field' => 'password', | |
'label' => 'パスワード', | |
'rules' => array( | |
'required', | |
array( | |
'isPassword', | |
function ($password) | |
{ | |
if($password === '') | |
{ | |
return TRUE; | |
} | |
if(preg_match('/\A(?=.*?[a-z])(?=.*?\d)[!-~]{8,100}+\z/i', $password)) | |
{ | |
return TRUE; | |
} | |
return FALSE; | |
} | |
) | |
), | |
'errors' => array( | |
'required' => '{field} は必須です', | |
'isPassword' => '{field} は8文字以上100文字以下の英数字記号で入力してください' | |
) | |
); | |
$passconf = array( | |
'field' => 'passconf', | |
'label' => 'パスワード(再入力)', | |
'rules' => 'matches[password]', | |
'errors' => array( | |
'matches' => '{field} が一致しません' | |
) | |
); | |
$name = array( | |
'field' => 'name', | |
'label' => 'ユーザーネーム', | |
'rules' => 'required|max_length[128]', | |
'errors' => array( | |
'required' => '{field} は必須です', | |
'max_length' => '{field} は128文字以下で入力してください' | |
) | |
); | |
//この名前で設定しておけばRegisterクラスのindexメソッドで勝手に呼び出されるようになります | |
$config['register/index'] = array($mail, $password, $passconf, $name); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class Register | |
* @property CI_Loader $load | |
* @property CI_Input $input | |
* @property CI_Form_validation $form_validation | |
* @property CI_Email $email | |
* @property CI_Config $config | |
* @property User_model $user_model | |
*/ | |
class Register extends CI_Controller | |
{ | |
/** | |
* Register constructor. | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
//バリデーションライブラリーのロード | |
$this->load->library('form_validation'); | |
//メールライブラリーのロード | |
$this->load->library('email'); | |
//ユーザーモデルのロード | |
$this->load->model('user_model'); | |
//フォームヘルパー・URLヘルパーのロード | |
$this->load->helper(array('form', 'url')); | |
} | |
/** | |
* メール設定を行い本登録用のメールを送信する | |
* @param $id | |
* @return bool | |
*/ | |
private function _sendMail($id) | |
{ | |
//現在のタイムスタンプを取得 | |
$time = time(); | |
//モデルからトークンを取得 | |
$token = $this->user_model->get_token($id, $time); | |
if ($token === '') | |
{ | |
return FALSE; | |
} | |
$this->email->from('example.com', 'hoge form'); | |
$this->email->to((string)$this->input->post('mail')); | |
$this->email->subject('本登録案内メール'); | |
$this->email->message($this->load->view('mail/register_user', compact('id', 'time', 'token'), TRUE)); | |
return $this->email->send(); | |
} | |
/** | |
* ユーザーデータを登録し、メール送信を行う | |
* どちらも成功した場合は空文字を返し、エラーが発生した場合はエラー文を返す | |
* @return string | |
*/ | |
private function _register_temp() | |
{ | |
//userテーブルへデータをINSERT | |
$id = $this->user_model->insert($this->input->post()); | |
//情報 or データベースにエラーが発生したら処理中断 | |
if ($id === 0) | |
{ | |
return 'データベースエラーが発生しました'; | |
} | |
//メール送信 | |
return $this->_sendMail($id) ? '' : 'メールの送信に失敗しました'; | |
} | |
/** | |
* フォームの表示・入力データの検証を行う | |
* 検証をクリアした場合はDB操作とメール送信担当メソッドを呼び出す | |
*/ | |
public function index() | |
{ | |
//エラー文 | |
$error = ''; | |
//もし$_POST['submit']が存在したらデータ入力後のアクセスと判定 | |
//config/form_validation.phpで設定したregisterバリデーション検証 | |
if ($this->input->post('submit') && $this->form_validation->run()) | |
{ | |
$error = $this->_register_temp(); | |
if ($error === '') | |
{ | |
//エラーが無かった=処理が成功した場合はリダイレクトする | |
redirect(site_url('register/complete')); | |
} | |
} | |
//view | |
$this->load->view('front/register_index', compact('error')); | |
} | |
/** | |
* 仮登録完了画面表示 | |
*/ | |
public function complete() | |
{ | |
$this->load->view('front/register_complete'); | |
} | |
/** | |
* URLからトークンを検証 | |
* OKならステータスを本登録に変更して完了画面を表示 | |
* @param int $id | |
* @param int $time | |
* @param string $token | |
*/ | |
public function auth($id, $time, $token) | |
{ | |
//現在時刻のタイムスタンプを取得 | |
$now_time = time(); | |
//timeがメール送信から一時間以内ではなかったらリダイレクト | |
//条件の前半はメール送信時間が現在より未来を弾く | |
//後半はメール送信時間が現在-1時間より過去を弾く | |
if ($now_time < $time || $time < $now_time - (int)$this->config->item('auth_time')) | |
{ | |
redirect(site_url()); | |
} | |
//トークンが不正だったらリダイレクト | |
if ($token !== $this->user_model->get_token($id, $time)) | |
{ | |
redirect(site_url()); | |
} | |
//ユーザーステータスをアップデート | |
$failure = ! $this->user_model->authentication($id); | |
//成功ページを表示 | |
$this->load->view('front/register_auth', compact('failure')); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @var bool $failure | |
*/ | |
defined('BASEPATH') OR exit('No direct script access allowed'); | |
?><!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0"> | |
<meta name="description" content=""> | |
<title>メール認証ユーザー登録フローテスト</title> | |
</head> | |
<body> | |
<h1>ユーザー登録</h1> | |
<?php if ($failure): ?> | |
<p>データベースエラーが発生しました。</p> | |
<p>開発者の次回アップデートにご期待ください。</p> | |
<?php else: ?> | |
<p>ユーザー登録が完了しました。</p> | |
<p>開発者がログイン機能を作るまでお待ちください。</p> | |
<?php endif; ?> | |
</body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
defined('BASEPATH') OR exit('No direct script access allowed'); | |
?><!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0"> | |
<meta name="description" content=""> | |
<title>メール認証ユーザー登録フローテスト</title> | |
</head> | |
<body> | |
<h1>ユーザー登録</h1> | |
<p>ユーザー認証メールを送信しました。</p> | |
<p>メールに記載されているURLへアクセスして登録を完了させてください。</p> | |
</body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @var string $error | |
*/ | |
defined('BASEPATH') OR exit('No direct script access allowed'); | |
?><!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0"> | |
<meta name="description" content=""> | |
<title>メール認証ユーザー登録フローテスト</title> | |
</head> | |
<body> | |
<h1>ユーザー登録</h1> | |
<?php echo validation_errors(); ?> | |
<?php echo $error; ?> | |
<?php echo form_open(); ?> | |
<div> | |
<label> | |
メールアドレス<?php echo form_input('mail', set_value('mail')); ?> | |
</label> | |
</div> | |
<div> | |
<label> | |
パスワード<?php echo form_password('password', set_value('password')); ?> | |
</label> | |
</div> | |
<div> | |
<label> | |
パスワード(再入力)<?php echo form_password('passconf', set_value('passconf')); ?> | |
</label> | |
</div> | |
<div> | |
<label> | |
ユーザーネーム<?php echo form_input('name', set_value('name')); ?> | |
</label> | |
</div> | |
<div> | |
<?php echo form_submit('submit', '認証メールを送信する'); ?> | |
</div> | |
<?php echo form_close(); ?> | |
</body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @var int $id | |
* @var int $time | |
* @var string $token | |
*/ | |
defined('BASEPATH') OR exit('No direct script access allowed'); | |
?>この度は、ご登録頂きありがとうございます。 | |
まだ登録は完了しておりません。 | |
以下のURLへアクセスして登録を完了させてください。 | |
<?php | |
echo site_url('register/auth/'.(string)$id.'/'.(string)$time.'/'.$token); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class User_model | |
* @property CI_Loader $load | |
* @property CI_DB $db | |
* @property CI_Config $config | |
*/ | |
class User_model extends CI_Model | |
{ | |
public function __construct() | |
{ | |
parent::__construct(); | |
//データベースのロード | |
$this->load->database(); | |
} | |
/** | |
* idから仮登録ユーザー情報を取得する | |
* 情報がなかった場合は空配列が返る | |
* @param int $id | |
* @return array | |
*/ | |
private function _get_temp_user($id) | |
{ | |
//クエリビルダでid指定されたユーザーを取得 | |
$this->db->where('id', $id); | |
$this->db->where('status', 0); | |
$query = $this->db->get('user', 1); | |
$result = $query->row_array(); | |
return $result ? $result : array(); | |
} | |
/** | |
* idからuserテーブルを参照してユーザー認証用のtokenを作成する | |
* $timeはタイムスタンプを設定 | |
* データが存在せず、場合は空文字を返す | |
* @param int $id | |
* @param int $time | |
* @return string | |
*/ | |
public function get_token($id, $time) | |
{ | |
$user = $this->_get_temp_user($id); | |
//ユーザー情報が存在しなかった場合は空文字(エラーの意)を返す | |
if ( ! isset($user['mail']) || ! isset($user['password'])) | |
{ | |
return ''; | |
} | |
//メールアドレス、、パスワード、タイムスタンプ、ソルトを組み合わせてハッシュを作成 | |
return hash('sha256', $user['mail'].'|'.$user['password'].'|'.$time.$this->config->item('salt')); | |
} | |
/** | |
* userテーブルに仮登録ステータスで情報をinsert する | |
* $dataに必要情報が無かった場合 or insertに失敗した場合は0を返す | |
* @param array $data | |
* @return int | |
*/ | |
public function insert($data) | |
{ | |
//データ内容の検証 | |
//今回はNULLを使わないのでOKだが、 | |
//もしNULLを明示的に入れる場合はissetで未定義判定ができないので注意 | |
$columns = array('mail', 'password', 'name'); | |
foreach ((array)$columns as $column) | |
{ | |
if ( ! isset($data[$column])) | |
{ | |
return 0; | |
} | |
} | |
//クエリビルダでINSERT | |
$result = $this->db->insert('user', array( | |
'mail' => $data['mail'], | |
'password' => hash('sha256', $data['password']), | |
'name' => $data['name'], | |
'status' => 0 | |
)); | |
return $result ? $this->db->insert_id() : 0; | |
} | |
/** | |
* ユーザーステータスを本登録にする | |
* エラーはFALSEを返す | |
* @param int $id | |
* @return bool | |
*/ | |
public function authentication($id) | |
{ | |
$user = $this->_get_temp_user($id); | |
//ユーザーが存在しない場合はFALSEを返す | |
if ( ! isset($user['id'])) | |
{ | |
return FALSE; | |
} | |
//statusカラムをUPDATE | |
return $this->db->update('user', array( | |
'status' => 1 | |
),array( | |
'id' => $id | |
)); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment