邮件验证
引言
许多 Web 应用在使用前都要求用户先验证他们的邮箱地址。Laravel 为此提供了便捷的内置服务,用于发送和验证邮箱验证请求,这样你就不必在每个项目中手动重复实现这一功能。
模型准备
在开始之前,请确认你的 App\Models\User 模型已经实现了 Illuminate\Contracts\Auth\MustVerifyEmail 接口:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable implements MustVerifyEmail
{
use Notifiable;
// ...
}
当你在模型中加入这个接口后,新注册的用户将会 自动收到一封包含邮箱验证链接的邮件。这是自动完成的,因为 Laravel 会为 Illuminate\Auth\Events\Registered 事件自动注册监听器 Illuminate\Auth\Listeners\SendEmailVerificationNotification。
如果你没有使用官方的 Starter Kit,而是在应用中 手动实现用户注册逻辑,请务必确保在用户成功注册后 手动触发 Registered 事件:
use Illuminate\Auth\Events\Registered;
event(new Registered($user));
这样才能保证验证邮件会正确发送给用户。
数据库准备
接下来,你的 users 表中必须包含一个 email_verified_at 字段,用来存储用户邮箱通过验证的日期和时间。通常,这个字段已经包含在 Laravel 默认提供的 0001_01_01_000000_create_users_table.php 数据库迁移文件中。
路由
为了正确实现邮箱验证,你需要定义三个路由。
- 第一,需要一个路由向用户展示提示页面,告诉他们应点击 Laravel 在注册后发送的验证邮件中的链接完成邮箱验证。
- 第二,需要一个路由来处理用户点击验证邮件中的链接时所发起的请求。
- 第三,需要一个路由用于在用户不小心遗失第一封验证邮件时,重新发送验证链接。
邮件验证通知
正如前文所述,你需要定义一个路由,用于返回一个视图,提示用户点击 Laravel 在注册后发送给他们的邮件中的邮箱验证链接。当用户在未先验证邮箱的情况下尝试访问应用的其他部分时,将会看到这个视图。请记住,只要你的 App\Models\User 模型实现了 MustVerifyEmail 接口,验证链接就会自动发送给用户:
Route::get('/email/verify', function () {
return view('auth.verify-email');
})->middleware('auth')->name('verification.notice');
返回邮箱验证提示页面的这个路由必须命名为 verification.notice。这一点非常重要,因为 Laravel 内置的 verified 中间件会在用户尚未验证邮箱时自动重定向到这个命名路由。
邮件验证处理器
接下来,我们需要定义一个路由,用于处理用户点击邮件中的邮箱验证链接后所发起的请求。这个路由应命名为 verification.verify,并使用 auth 和 signed 中间件:
use Illuminate\Foundation\Auth\EmailVerificationRequest;
Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
$request->fulfill();
return redirect('/home');
})->middleware(['auth', 'signed'])->name('verification.verify');
在继续之前,我们先更详细地看看这个路由。
首先,你会注意到这里使用的是 EmailVerificationRequest 类型的请求,而不是常见的 Illuminate\Http\Request。
EmailVerificationRequest 是 Laravel 自带的一个表单请求类,它会自动验证 URL 中 id 和 hash 参数的有效性,无需你手动编写验证逻辑。
接下来,我们可以直接调用 $request->fulfill() 方法。
这个方法会:
- 调用已认证用户的
markEmailAsVerified方法,标记邮箱为已验证; - 触发
Illuminate\Auth\Events\Verified事件。
markEmailAsVerified 方法通过 Illuminate\Foundation\Auth\User 基类提供给默认的 App\Models\User 模型。
邮箱验证成功后,你可以将用户重定向到任何你希望的页面。
重发验证邮件
有时用户可能会丢失或误删邮箱验证邮件。为了处理这种情况,你可以定义一个路由,允许用户请求重新发送验证邮件。然后,你可以在邮箱验证提示页面中放置一个简单的表单按钮,对该路由发起请求:
use Illuminate\Http\Request;
Route::post('/email/verification-notification', function (Request $request) {
$request->user()->sendEmailVerificationNotification();
return back()->with('message', 'Verification link sent!');
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');
路由保护
你可以使用路由中间件来限制只有通过邮箱验证的用户才能访问某个路由。Laravel 提供了一个 verified 中间件别名,它实际上指向 Illuminate\Auth\Middleware\EnsureEmailIsVerified 中间件类。由于 Laravel 已经自动注册了这个别名,你只需要将 verified 中间件附加到路由定义上即可。通常它会与 auth 中间件一起使用:
Route::get('/profile', function () {
// 只有通过邮箱验证的用户才能访问此路由...
})->middleware(['auth', 'verified']);
如果未验证邮箱的用户尝试访问使用了该中间件的路由,他们将会被自动重定向到名为 verification.notice 的路由。
自定义
自定义验证邮件
虽然 Laravel 默认提供的邮箱验证通知已经能满足大部分应用的需求,但如果你希望自定义验证邮件的内容,Laravel 也为你提供了相应的扩展能力。
首先,你可以通过向 Illuminate\Auth\Notifications\VerifyEmail 通知类提供的 toMailUsing 方法传递一个闭包来自定义邮件内容。该闭包会接收两个参数:
- $notifiable:正在接收通知的模型实例;
- $url:用户必须访问以完成邮箱验证的签名验证链接。
闭包应返回一个 Illuminate\Notifications\Messages\MailMessage 实例。通常,你应该在应用的 AppServiceProvider 类的 boot 方法中调用 toMailUsing:
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Notifications\Messages\MailMessage;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// ...
VerifyEmail::toMailUsing(function (object $notifiable, string $url) {
return (new MailMessage)
->subject('Verify Email Address')
->line('Click the button below to verify your email address.')
->action('Verify Email Address', $url);
});
}
事件
在使用 Laravel 应用启动包(starter kits)时,Laravel 会在邮箱验证过程中自动触发 Illuminate\Auth\Events\Verified 事件。如果你是在手动实现邮箱验证功能,则可能需要在用户完成验证后手动触发这些事件。