首先为 users
表添加两个字段:
verification_token
verified
创建迁移文件:
php artisan make:migration add_verification_to_users_table --table=users
添加字段:
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
下面开始逻辑部份:
- 用户注册后,发送激活邮件
- 每位用户登录,中间件验证用户是否激活
首先在用户注册时,就要为用户的 verification_token
字段生成30位的随机数,这里使用观察者模式中
的 creating
方法,此方法在用户创建过程中会被触发。利用这个特性,将逻辑写入到 UserObserver
的creating
方法中。这里假设你已提前创建好这个观察者类
/**
* 为注册用户生成邮箱验证字段
* @param User $user
*/
public function creating(User $user) {
$user->verification_token = Str::random(30);
}
参数 User
是自动传入进来的与 Observer
是提前绑定好的。
这样在用户注册时就完成了邮箱验证码的创建.
更改你的 store
方法:
public function store(UserStoreRequest $r): Response {
$credentials = $r->validated();
$credentials['password'] = password_hash($credentials['password'],PASSWORD_DEFAULT);
$user = User::query()->create($credentials);
if (!is_null($user)) {
return $this->services->json('注册成功,验证邮件已发送至您的邮箱');
}else {
return $this->services->json('注册失败',403);
}
}
解释下上面的代码:首先是获取用户提交注册字段,使用 User
下的 create
方法进行创建一个模型,
如果创建成功会返回一个 Model
类实例,否则为 null
,接下来再次判断创建的实例是为一个 model
,
则返回一个注册成功的 json
消息!
注册方法完成后,我们将逻辑移动到邮箱发送与邮件激活方法上。邮箱发送这里使用了队列,这里不再过多阐述
既然是激活用户,那就要用到对应用户的 verification_token
,下面依次创建激活路由、激活控制器
激活路由
Route::get('/email_verification/{token}',[AuthController::class,'verifiedEmail'])->name('verified_email');
这里要注意路由方法一定要使用 get
方法。因为后面显示的使用 get
去提交 token
对应控制器 **AuthController**
public function verifiedEmail($token) {
$user = User::query()->where('verification_token',$token)->first();
$user->forceFill([
'verified'=>true,
'verification_token'=>null,
])->save();
return $this->services->json('恭喜您,邮箱验证成功!');
}
邮箱部份:
@component('mail::message')
<h1>欢迎注册啄木鸟社区</h1>
<h4>{{$name}}你好,你的邮箱件为: {{$email}},请点击激活按钮激活您的帐户</h4>
@component('mail::button',['url'=>route('verified_email',$token)])
激活
@endcomponent
Thanks,<br>
{{ config('app.name') }}
@endcomponent
中间件拦截
试想一下,你可能要求每位用户在登录时都检查他是否激活邮箱,但控制器做的事太多了。能不能把
用户是否激活这个逻辑放到中间件,让中间件去消费它呢?答案是可以的。
创建中间件:
php artisan make:middleware UserEmailVerified
中件间件里不允许返回 json_encode
,所以我们自定义一个异常类。当达到拦截条件时,抛出这个异常,让异常替
我们返回这些错误.
创建异常:
php artisan make:exception UserEmailVerifiedException
中间件内容:
class UserEmailVerified {
/**
* Handle an incoming request.
* 用户是否进行了邮箱激活
* @param Request $request
* @param Closure $next
* @return mixed
* @throws UserEmailVerifiedException
*/
public function handle(Request $request, Closure $next): mixed {
$user = User::query()->where('email', $request->email)->first();
if (is_null($user)) {
throw new UserEmailVerifiedException('帐号不存在,请注册', 403);
} else if (!$user->verifind && !empty($user->verification_token)) {
throw new UserEmailVerifiedException("邮箱未激活,请前往{$user->email}查收激活邮件", 403);
}
return $next($request);
}
}
上面代码中,首先通过 request
传入过来的邮箱获取对应用户,再判断 verified
是否为 true
,verification_token
是否为空。当然,我这里用的是取反判断。
注册中间件
protected $routeMiddleware = [
//...
'auth.email'=>\App\Http\Middleware\UserEmailVerified::class, //邮箱激活验证
];
使用路由: Login
异常类 **UserEmailVerifiedException**
<?php
namespace App\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
use Illuminate\Http\JsonResponse;
class UserEmailVerifiedException extends Exception
{
private string $msg;
private int $status;
#[Pure]
public function __construct($message = "", $code = 0) {
$this->msg= $message;
$this->status = $code;
parent::__construct($this->msg, $this->status);
}
public function render(): JsonResponse {
return response()->json([
'message'=>$this->msg,
],$this->status);
}
}
代码中多次出现: $this->services->json()
此方法为二次封装的 Response()->json()