Laravel 8中如何将Jetstream邮箱认证改为手机号认证?
嘿,我来一步步带你把Laravel 8 + Jetstream的邮箱认证换成手机号认证,其实整个流程拆解开就很清晰,跟着下面的步骤来就行:
首先得给用户表加上手机号相关的字段,我们需要三个核心字段:存储手机号的phone、标记手机号是否验证的phone_verified_at,还有临时存验证码的verification_token。
- 生成迁移文件:
php artisan make:migration add_phone_verification_fields_to_users_table
- 打开生成的迁移文件,填入以下代码:
public function up() { Schema::table('users', function (Blueprint $table) { $table->string('phone')->unique()->after('email'); $table->timestamp('phone_verified_at')->nullable(); $table->string('verification_token')->nullable(); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn(['phone', 'phone_verified_at', 'verification_token']); }); }
- 运行迁移生效:
php artisan migrate
接下来要修改User模型,适配手机号验证的逻辑:
- 更新
$fillable数组,加入新增的字段:
protected $fillable = [ 'name', 'phone', 'password', 'verification_token', ];
- 更新
$casts数组,把邮箱验证的字段换成手机号的:
protected $casts = [ 'phone_verified_at' => 'datetime', 'password' => 'hashed', ];
- 添加手机号验证相关的方法,替换原来的邮箱验证方法:
// 检查手机号是否已验证 public function hasVerifiedPhone() { return !is_null($this->phone_verified_at); } // 标记手机号为已验证 public function markPhoneAsVerified() { return $this->forceFill([ 'phone_verified_at' => $this->freshTimestamp(), ])->save(); } // 发送手机号验证通知 public function sendPhoneVerificationNotification() { // 生成6位随机验证码 $token = rand(100000, 999999); $this->verification_token = $token; $this->save(); $this->notify(new \App\Notifications\VerifyPhone($token)); }
现在需要一个通知类来发送短信验证码,我们用Laravel的通知系统来实现:
- 生成通知类:
php artisan make:notification VerifyPhone
- 打开
app/Notifications/VerifyPhone.php,替换成以下内容(这里以Vonage短信服务商为例,你可以换成自己用的服务商,比如阿里云/腾讯云短信):
namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\VonageMessage; use Illuminate\Notifications\Notification; class VerifyPhone extends Notification { use Queueable; protected $token; public function __construct($token) { $this->token = $token; } public function via($notifiable) { return ['vonage']; // 换成你的短信驱动 } public function toVonage($notifiable) { return (new VonageMessage) ->content("你的Laravel账号验证码是:{$this->token},请在5分钟内完成验证。"); } }
- 在
.env文件里配置短信服务商的密钥(以Vonage为例):
VONAGE_KEY=你的Vonage密钥 VONAGE_SECRET=你的Vonage密钥
Jetstream底层用Fortify处理认证,我们需要修改注册、登录的逻辑:
1. 修改注册逻辑
打开app/Actions/Fortify/CreateNewUser.php,把邮箱相关的验证和存储换成手机号:
public function create(array $input) { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], 'phone' => ['required', 'string', 'max:20', 'unique:users'], 'password' => $this->passwordRules(), ])->validate(); $user = User::create([ 'name' => $input['name'], 'phone' => $input['phone'], 'password' => Hash::make($input['password']), ]); // 注册后自动发送验证短信 $user->sendPhoneVerificationNotification(); return $user; }
2. 修改登录逻辑
打开app/Actions/Fortify/AttemptToAuthenticate.php,把登录字段从email换成phone:
protected function attemptLogin($request) { return $this->guard->attempt( ['phone' => $request->phone, 'password' => $request->password], $request->filled('remember') ); }
3. 更新Fortify配置
打开app/Providers/FortifyServiceProvider.php,在boot方法里设置认证字段为手机号:
public function boot() { // ... 保留其他原有代码 // 自定义认证逻辑,用手机号登录 Fortify::authenticateUsing(function (Request $request) { $user = User::where('phone', $request->phone)->first(); if ($user && Hash::check($request->password, $user->password)) { return $user; } }); }
Jetstream的视图默认在resources/views/auth目录下,我们需要把邮箱相关的输入框换成手机号:
1. 修改注册视图
打开resources/views/auth/register.blade.php,找到邮箱输入框的部分,替换成:
<div> <x-label for="phone" value="{{ __('手机号') }}" /> <x-input id="phone" class="block mt-1 w-full" type="text" name="phone" :value="old('phone')" required autofocus /> </div>
2. 修改登录视图
打开resources/views/auth/login.blade.php,同样替换邮箱输入框:
<div> <x-label for="phone" value="{{ __('手机号') }}" /> <x-input id="phone" class="block mt-1 w-full" type="text" name="phone" :value="old('phone')" required autofocus /> </div>
3. 创建手机号验证页面
新建resources/views/auth/verify-phone.blade.php,用来展示验证码输入表单:
<x-guest-layout> <x-authentication-card> <x-slot name="logo"> <x-authentication-card-logo /> </x-slot> <div class="mb-4 text-sm text-gray-600"> {{ __('感谢注册!请输入我们发送到你手机上的6位验证码完成验证。') }} </div> @if (session('error')) <div class="mb-4 font-medium text-sm text-red-600"> {{ session('error') }} </div> @endif <x-validation-errors class="mb-4" /> <form method="POST" action="{{ route('verification.verify') }}"> @csrf <div> <x-label for="token" value="{{ __('验证码') }}" /> <x-input id="token" class="block mt-1 w-full" type="text" name="token" required autofocus /> </div> <div class="flex items-center justify-end mt-4"> <x-button type="submit" class="ml-4"> {{ __('验证') }} </x-button> </div> </form> </x-authentication-card> </x-guest-layout>
1. 添加验证路由
打开routes/web.php,添加手机号验证的路由:
use App\Http\Controllers\Auth\VerificationController; Route::get('/verify-phone', [VerificationController::class, 'show'])->name('verification.notice'); Route::post('/verify-phone', [VerificationController::class, 'verify'])->name('verification.verify');
2. 创建验证控制器
生成VerificationController处理验证逻辑:
php artisan make:controller Auth/VerificationController
打开app/Http/Controllers/Auth/VerificationController.php,填入以下代码:
namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Http\Request; class VerificationController extends Controller { public function __construct() { $this->middleware('auth'); } public function show() { // 如果已经验证过,直接跳转到仪表盘 if (auth()->user()->hasVerifiedPhone()) { return redirect()->route('dashboard'); } return view('auth.verify-phone'); } public function verify(Request $request) { $request->validate([ 'token' => 'required|digits:6', ]); $user = auth()->user(); // 验证验证码是否匹配 if ($user->verification_token === $request->token) { $user->markPhoneAsVerified(); $user->verification_token = null; $user->save(); return redirect()->route('dashboard')->with('success', __('手机号验证成功!')); } return back()->with('error', __('验证码无效,请重新输入。')); } }
3. 替换验证中间件
Laravel默认的verified中间件是检查邮箱验证的,我们需要改成检查手机号:
- 复制
app/Http/Middleware/EnsureEmailIsVerified.php,重命名为EnsurePhoneIsVerified.php - 修改文件内容:
namespace App\Http\Middleware; use Closure; use Illuminate\Contracts\Auth\MustVerifyPhone; use Illuminate\Http\Request; class EnsurePhoneIsVerified { public function handle(Request $request, Closure $next, $redirectToRoute = null) { if (! $request->user() || ($request->user() instanceof MustVerifyPhone && ! $request->user()->hasVerifiedPhone())) { return $request->expectsJson() ? abort(403, '你的手机号尚未验证。') : redirect()->route('verification.notice'); } return $next($request); } }
- 打开
app/Http/Kernel.php,把verified中间件替换成我们新的:
protected $routeMiddleware = [ // ... 其他中间件 'verified' => \App\Http\Middleware\EnsurePhoneIsVerified::class, ];
最后,清理一下缓存,然后测试注册、登录、验证的完整流程:
php artisan config:clear php artisan view:clear
现在你就可以用手机号完成注册、登录和验证了!如果需要调整短信服务商,只需要修改VerifyPhone通知类里的via方法和对应的发送逻辑就行。
内容的提问来源于stack exchange,提问作者Porush Manjhi

