首先为 users 表添加两个字段:

    • verification_token
    • verified

    创建迁移文件:

    1. 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()