Userテーブルにverifiedフラグを追加
今回作成するログインでは、デフォルトで作成されるuserテーブルには仮登録に必要なカラムが入っていないため、それを追加する。
また、仮登録から本登録へ移行する際、登録時のメールアドレス宛にURLを送付し、クリックすることで本登録が完了とするものとする。
そのため本登録対象を把握するため、トークン用のカラムを追加する。
ただし、URLの有効期限は1時間としたい。そのためのカラムはデフォルトではdatetime型の「email_verified_at」カラムが予め設定されているため、これを使用するものとする。
・ユーザテーブルの追加
artisanにて「modify_users_table」編集ファイルの雛形を追加。
php artisan make:migration modify_users_table --table=users
追加した「database/migrations/xxxx_modify_users_table.php」にて、
「updated_at」カラムの後に「email_token」カラムを追加、
「email_token」カラムの後に「verified」カラムを追加する。
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('email_token')->after('updated_at')->nullable();
$table->tinyInteger('verified')->after('email_token')->default(0);
});
}
編集後、artisanにてテーブルに反映させる。
php artisan migrate
Modelの編集
追加した「email_token」「verified」と既存の「email_verified_at」カラムにユーザが登録変更を行えるようにするため、Modelの「app/Models/User.php」に追加する。
protected $fillable = [
'name',
'email',
'password',
'email_token',
'verified',
'email_verified_at',
];
メール認証用クラスを作成
artisaにてメール認証クラスを作成する。
php artisan make:mail EmailVerification
ここより「app/Mail/EmailVerification.php」ファイルを編集し、メール認証を作成する。
メール送信時の変数を受け取るため、必要な変数を追加する。
public $user;
public $text = 'email.verification';
public $htmlString;
public $with = ['text' => ''];
public $subject;
constructにてuser情報と件名(subject)を設定する。subjectは日本語ファイル(ja.json)を活用し、賢明はここで設定することとする。
public function __construct($user)
{
$this->user = $user;
$path = resource_path() . '/lang/ja.json';
if(file_exists($path)){
$json = file_get_contents($path);
$json = json_decode($json);
$this->subject = $json->VerificationSubject;
}
}
取得した件名をセットする。
public function envelope()
{
return new Envelope(
subject: $this->subject
);
}
メール本文はviewから取得するため、contentメソッドへview(email.verification)の設定をすると共に、認証用のURLは登録者ごとのトークンが発行されるため、本文内のURLに対しトークン(email_token)をセットする。
public function content()
{
return new Content(
view: 'email.verification',
with: [
'email_token' => $this->user->email_token,
]
);
}
本文用のviewを作成するため、「resources/views/」へ「email」フォルダを作成し、その中に「resources/views/email/email.blade.php」ファイルを作成する。
メール本文は認証用のURLを含めた内容とするが、ここで日本語セットを活用したいため英語本文とした。
トークン用のURLは「/auth/verifyemail/」とし、「email_token」
<h1>{{ __('Notification of email address verification.') }}</h1>
<p>{{ __('Please click on the following link to verify your email address.') }}<br>
{{url('/auth/verifyemail/'.$email_token)}} </p>
<p>{{ __('If you have no idea what it is, please ignore it.') }}</p>
メール送信用のキュージョブを作成
artisaでqueue job作成する。
php artisan make:job SendVerificationEmail
「app/Jobs/SendVerificationEmail.php」ファイルを編集する。
メール送信用クラスと、先ほど作成したEmailVerificationクラスを追加する。
use Illuminate\Support\Facades\Mail;
use App\Mail\EmailVerification;
ユーザ情報用の変数を追加する。
protected $user;
constructメソッドにユーザ情報をセットする。
public function __construct($user)
{
$this->user = $user;
}
ジョブに送信処理を追加する。
public function handle()
{
$email = new EmailVerification($this->user);
Mail::to($this->user->email)->send($email);
}
会員登録用のコントローラを編集
「app/Http/Controllers/Auth/RegisterController.php」ファイルへクラスを追加する。
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use App\Jobs\SendVerificationEmail;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
RegisterControllerクラスに「RegistersUsers」クラスを追加する。
class RegisterController extends Controller
{
use RegistersUsers;
createメソッドへトークン用の「email_token」と登録時の日時保存する「email_verified_at」を追加する。トークンはemailをbase64でエンコードしたものを設定するが、複雑化するために余分にランダム英数字を追加したほうが無難。
日時はCarbonクラスにて追加する。
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'email_token' => base64_encode($data['email']),
'email_verified_at' => Carbon::now(),
]);
}
registerメソッドに先ほど作成した「SendVerificationEmail」クラスのジョブを追加する。
public function register(Request $request)
{
$this->validator($request->all())->validate();
event(new Registered($user = $this->create($request->all())));
dispatch(new SendVerificationEmail($user));
return view('auth.verification');
}
verifyメソッドへ本登録処理を追加する。
「verified」カラムが0であれば仮登録ユーザとし、「verified」カラムを1にアップデートし、本登録処理を行う。ただし、URLの有効期限を1時間とし、1時間以上の場合はデータを削除する。
有効期限切れの場合のviewは「auth.warning」とし、本登録が完了した場合のviewは「auth.emailconfirm」とする。
登録データがない場合、トップページに遷移する。
public function verify($token)
{
$user = User::where('email_token', $token)->where('verified', 0)->first();
// 登録があれば処理
if(isset($user->id) && $user->id <> NULL){
// 現在の時刻を取得
$date_now = new Carbon(Carbon::now());
// メールの送信時刻を取得し,1時間加えた時刻を有効期限とする
$date_expire = Carbon::createFromFormat('Y-m-d H:i:s', $user->email_verified_at);
$date_expire->addHour(1);
// リンクの有効期限のチェック
if ($date_now->gt($date_expire)) {
DB::table('users')->where('email_token', $token)->delete();
return view('auth.warning');
}
// 仮登録を本登録にする
else if(DB::table('users')->where('email_token', $token)->update(['verified' => 1])){
return view('auth.emailconfirm', ['user' => $user]);
}
}
// 登録がない場合はトップページに遷移
else{
return redirect(url('/'));
}
}
ログイン用のコントローラを編集
ログイン時、仮登録であればログインを拒否したいため、「attemptLogin」メソッドを追加し、「verified」カラムが1の場合のみ認証するようにする。
function attemptLogin(\Illuminate\Http\Request $request)
{
$email = $request->input('email');
$password = $request->input('password');
$credentials = ['email' => $email, 'password' => $password, 'verified' => 1];
return $this->guard()->attempt($credentials, $request->filled('remember'));
}
loginメソッドにて、先ほど作成した「attemptLogin」メソッドで認証した場合、ログインページへ遷移し、それ以外はログイン画面に遷移するようにする。
public function login(Request $request)
{
$this->validateLogin($request);
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}else{
redirect()->route('login');
}
}
Viewの作成
会員仮登録後のページを作成するため、「resources/views/auth/verification.blade.php」ファイルを作成し、英語表記にてページを作成する。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<h1>{{ __('User Registration') }}</h1>
<p>{{ __('User Registration.') }}</p>
<p>{{ __('An email has been sent to you for confirmation, please click on the link in the email.') }}</a></p>
</div>
</div>
</div>
</div>
@endsection
メール送信後、リンクをクリックした際の認証用ページを作成するため、「resources/views/auth/emailconfirm.blade.php」ファイルを作成し、英語表記にてページを作成する。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<h1>{{ __('Confirmation complete') }}</h1>
<p>{{ __('Your email address has been verified.') }}</p>
<p><a href="{{url('/login')}}">{{ __('Please log in here.') }}</a></p>
</div>
</div>
</div>
</div>
@endsection
ルートを定義
認証用リンククリック時のルートを定義するため、「routes/web.php」の最後に定義追加する。
Route::get('/auth/verifyemail/{token}', [RegisterController::class, 'verify']);
日本語ファイルを追加する
「resources/lang/ja.json」ファイルへ日本語ファイルを追加する。これまで英語表記にしていたため、それらを日本語に変更する。
{
"User Registration": "ユーザ登録",
"User Registration.": "ユーザ登録を行いました。",
"An email has been sent to you for confirmation, please click on the link in the email.": "確認のためにEメールを送信しましたので、メールのリンクをクリックしてください。",
"Confirmation complete": "確認完了",
"Your email address has been verified.": "あなたのメールアドレスが確認できました。",
"Please log in here.": "ここからログインしてください。",
"VerificationSubject": "メール認証",
"Verify error": "有効期限エラー",
"Email link expiration date (1 hour) has passed.": "メールリンクの有効期限(1時間)を過ぎました。",
"Please register again.": "再度会員登録を行なってください。",
"Click here for new registration.": "新規登録はこちら。",
"Notification of email address verification.": "メールアドレスの認証のお知らせ。",
"Please click on the following link to verify your email address.": "以下のリンクをクリックして、メールアドレスを認証をしてください。",
"If you have no idea what it is, please ignore it.": "心当たりが無い場合は、無視してください。"
}