Laravel 專案實現郵箱驗證功能

sevdot發表於2018-08-28

郵箱驗證功能在很多網站都有用到,我的個人網站上也用到,郵箱驗證需要用到郵件傳送服務,Laravel 提供了非常簡單的郵件傳送 API,Laravel 專案實現郵件傳送功能很簡單,下面我開始介紹一下我的具體實現。

郵箱驗證

郵箱驗證功能實現具體步驟如下:

  1. 新增欄位,為使用者表新增兩個欄位,驗證令牌 (verification_token) 和驗證狀態 (verified) 。
  2. 生成令牌。
  3. 使用者登入後,檢測是否驗證過郵箱,沒有則引導去驗證。
  4. 使用阿里雲企業郵箱傳送郵箱驗證連結。
  5. 使用者點選郵箱驗證連結完成驗證。

新增欄位

使用命令生成遷移檔案,命令如下:

$ php artisan make:migration add_verification_to_users_table --table=users

命令會在 database/migrations 目錄下生成遷移檔案 [timestamp]\_add\_verification\_to\_users\_table.php,修改遷移檔案,修改後如下:

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddVerificationToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('verification_token')->nullable();
            $table->boolean('verified')->default(false);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('verification_token');
            $table->dropColumn('verified');
        });
    }

接著我執行遷移,將欄位加入到使用者表中。執行命令如下:

php artisan migrate

生成令牌

我希望使用者註冊的時候就給使用者生成令牌,所以我在 App 目錄下建立 Observers 目錄,並在 Observers 目錄下建立 UserObserver.php 檔案,並編寫如下程式碼:

namespace App\Observers;

use App\Models\Article;
use App\Models\Comment;
use App\Models\Reply;
use App\Models\User;

class UserObserver
{
    public function creating(User $user)
    {
        $user->verification_token=str_random(30);
    }
}

app/Providers 目錄下的 AppServiceProvider.php 檔案中 註冊監聽 Eloquent 事件,程式碼如下:

namespace App\Providers;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        \App\Models\User::observe(\App\Observers\UserObserver::class);
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

使用者登入後,檢測是否驗證過郵箱,沒有則引導去驗證

使用者登入後,會檢測郵箱是否驗證,如果沒有驗證就會出現郵箱未驗證的提示,並引導使用者去驗證。效果圖如下:

個人網站測試網站截圖 | left

實現引導的程式碼如下:

@if(Auth::check() && !Auth::user()->verified&&!Request::is('email_verification_required'))
        <article class="message is-warning">
            <div class="message-body">
                郵箱未啟用,請前往 {{ Auth::user()->email }} 查收啟用郵件,啟用後才能完整地使用社群功能,如發帖和回帖。未收到郵件?請前往 <a class="has-text-danger" href="{{ route('email-verification-required') }}">重發郵件</a> 。
            </div>
        </article>
@endif

設定路由,修改 routes 目錄下 web.php 檔案,增加一個路由,程式碼如下:

Route::get('/email_verification_required', 'UsersController@emailVerificationRequired')->name('email-verification-required');

接下來為使用者控制器定義一個 emailVerificationRequired 方法,該方法將用於使用者點選引導連結進入郵件傳送頁面,頁面效果如下:

個人網站測試網站截圖 | left

emailVerificationRequired 方法具體實現程式碼如下:

app/Http/Controllers/UsersController.php

    public function emailVerificationRequired(){
        return view('users.edit_email_notify');
    }

實現頁面程式碼如下:

resources/views/users/edit_email_notify.blade.php

@extends('layouts.app')
@section('title', '傳送郵箱驗證')

@section('content')
    <section class="section">
        <div class="container">
            <div class="columns">
                <div class="column is-offset-4 is-4">
                    <div class="card">
                        <div class="card-content ">

                            <h2><i class="fa fa-cog" aria-hidden="true"></i> 驗證郵箱</h2>
                            <hr>
                            <article class="message is-warning">
                                <div class="message-body">
                                    <p>郵箱未啟用,請前往 {{ Auth::user()->email }} 查收啟用郵件,啟用後才能完整地使用網站功能,如評論、訂閱專欄。</p>
                                    <br>
                                    <p> 未收到郵件?請點選以下按鈕重新傳送驗證郵件。</p>
                                </div>
                            </article>
                            <form class="form-horizontal" method="POST" action="{{ route('users.send-verification-mail')}}" accept-charset="UTF-8">

                                {!! csrf_field() !!}

                                <div class="">
                                    <button class="button is-link is-fullwidth"><i class="fa fa-paper-plane" aria-hidden="true"></i> 重新傳送驗證郵件</button>
                                </div>
                                <br>
                            </form>
                        </div>

                    </div>
                </div>
            </div>
        </div>
    </section>
@stop

使用阿里雲企業郵箱傳送郵箱驗證連結。

接下來我們要開始使用郵箱傳送功能,在 Laravel 中,可以通過 Mail 介面的 send 方法來進行郵件傳送,示例如下:

$view = 'emails.email_verification';
$data = compact('user');
$from = 'aufree@yousails.com';
$name = 'Aufree';
$to = $user->email;
$subject = "感謝註冊 Sample 應用!請確認你的郵箱。";

Mail::send($view, $data, function ($message) use ($from, $name, $to, $subject) {
    $message->from($from, $name)->to($to)->subject($subject);
});

Laravel 中郵箱傳送的配置存放於 config/mail.php 中。不過 mail.php 中我們所需的配置,都可以通過 .env 來配置。作為最佳實踐,我們優先選擇通過環境變數來配置:
.env

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mxhichina.com
MAIL_PORT=25
MAIL_USERNAME=xxxxxxxxxxxxxx@sevdot.com
MAIL_PASSWORD=xxxxxxxxx
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=xxxxxxxxxxxxxx@sevdot.com
MAIL_FROM_NAME=SevDot

選項 MAIL_USERNAMEMAIL_FROM_ADDRESS 需儲存一致,為郵箱賬號,MAIL_PASSWORD 為郵箱的登入密碼。

設定路由,修改 routes 目錄下 web.php 檔案,增加一個路由,程式碼如下:

Route::post('/users/send_verification_mail', 'UsersController@sendVerificationMail')->name('users.send-verification-mail');

接下來為使用者控制器定義一個 sendVerificationMail 方法,該方法將用於傳送郵件給指定使用者。具體程式碼實現如下:

app/Http/Controllers/UsersController.php

 public function sendVerificationMail(){
        $user= Auth::user();
        $this->sendEmailConfirmationTo($user);
        session()->flash('success', '驗證郵件已傳送到您的註冊郵箱上,請注意查收。');
        return redirect('/');
    }
  protected function sendEmailConfirmationTo($user)
    {
        $view = 'emails.email_verification';
        $data = compact('user');
        $to = $user->email;
        $subject = "感謝註冊 SevDot,請驗證郵箱";

        Mail::send($view, $data, function ($message) use ($to, $subject) {
            $message->to($to)->subject($subject);
        });
    }

在 Laravel 中,我們使用檢視來構建郵件模板,在使用者查收郵件時,該模板將作為內容展示檢視。接下來我們需要建立一個用於渲染註冊郵件的 email_verification 檢視。

resources/views/emails/email_verification.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>註冊確認連結</title>
</head>
<body>
<h1>感謝您在 SevDot 網站進行註冊!</h1>

<p>
    請點選下面的連結完成註冊:
    <a href="{{ route('verified_email',$user->verification_token) }}">
        {{ route('verified_email', $user->verification_token) }}
    </a>
</p>

<p>
    如果這不是您本人的操作,請忽略此郵件。
</p>
</body>
</html>

到此郵件傳送功能已經實現,使用者點選傳送驗證郵箱按鈕,就會收到郵件,使用者可以進入郵箱,點選郵件連結驗證郵箱。

使用者點選郵箱驗證連結完成驗證。

上面我們已經成功傳送郵件,使用者會收到郵件,使用者點選進行驗證郵箱,我們繼續完成驗證郵箱功能。

設定路由,修改 routes 目錄下 web.php 檔案,增加一個路由,程式碼如下:

routes/web.php

Route::get('/email_verification/{token}', 'UsersController@verifiedEmail')->name('verified_email');

接下來為使用者控制器定義一個 verifiedEmail 方法,該方法將用於驗證郵箱。具體程式碼實現如下:

app/Http/Controllers/UsersController.php

    public function verifiedEmail($token){
        $user = User::where('verification_token',$token)->first();
        $user->verified = true;
        $user->verification_token = null;
        $user->save();
        Auth::login($user);
        session()->flash('success', '恭喜您,郵箱驗證成功!');
        return redirect('/');
    }

至此郵箱驗證功能已經全部完成,但是可能會出現一些問題。

FAQ

1. 本地開發完成,本地可以正常傳送郵件,但是上傳至阿里雲伺服器以後不能傳送郵件

原因是阿里雲禁用了 25 埠,解決方案是改 .env 檔案,將 MAIL_PORT=25 改為 MAIL_PORT=465,將 MAIL_ENCRYPTION=tls 改為 MAIL_ENCRYPTION=ssl

相關文章