在 Laravel 12 中实现手机号验证,可以通过以下步骤完成完整的验证流程(包含格式验证、唯一性验证和短信验证):
1. 数据库准备
确保 users
表包含已验证手机号字段:
// database/migrations/xxxxx_add_phone_to_users_table.php
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->nullable()->comment('手机号');
$table->timestamp('phone_verified_at')->nullable()->comment('验证时间');
});
2. 创建验证规则
方法 1:使用 FormRequest
创建注册验证请求类:
php artisan make:request RegisterRequest
app/Http/Requests/RegisterRequest.php
public function rules()
{
return [
'phone' => [
'required',
'regex:/^\+?[1-9]\d{8,14}$/', // 国际手机号格式(含国家码)
'unique:users,phone'
],
'sms_code' => 'required|digits:6'
];
}
public function messages()
{
return [
'phone.regex' => '手机号格式无效,示例:+8613123456789',
'sms_code.digits' => '验证码必须是6位数字'
];
}
方法 2:直接验证
在控制器中验证:
$validated = $request->validate([
'phone' => 'required|regex:/^\+?[1-9]\d{8,14}$/|unique:users,phone',
'sms_code' => 'required|digits:6'
]);
3. 手机号预处理中间件(可选)
自动清理格式:
// app/Http/Middleware/FormatPhone.php
public function handle(Request $request, Closure $next)
{
if ($request->has('phone')) {
$phone = preg_replace('/[^0-9+]/', '', $request->phone);
$request->merge(['phone' => $phone]);
}
return $next($request);
}
注册中间件到 Kernel.php
:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\FormatPhone::class,
// ...
]
];
4. 短信验证集成
发送验证码接口
// routes/api.php
Route::post('/send-sms', function (Request $request) {
$request->validate(['phone' => 'required|regex:/^\+?[1-9]\d{8,14}$/']);
// 生成6位随机码并缓存(有效期5分钟)
$code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
Cache::put('sms_code:'.$request->phone, $code, 300);
// 调用短信服务(示例用阿里云)
AlibabaCloud::sms()
->send()
->withPhoneNumbers($request->phone)
->withSignName('YourApp')
->withTemplateCode('SMS_123456')
->withTemplateParam(json_encode(['code' => $code]))
->send();
return response()->json(['message' => '验证码已发送']);
});
验证码校验逻辑
在控制器中添加:
protected function verifySmsCode($phone, $code)
{
$cachedCode = Cache::pull('sms_code:'.$phone);
return $cachedCode && $cachedCode === $code;
}
5. 验证流程示例
注册控制器逻辑
// app/Http/Controllers/Auth/RegisterController.php
public function register(RegisterRequest $request)
{
// 验证短信验证码
if (!$this->verifySmsCode($request->phone, $request->sms_code)) {
return back()->withErrors(['sms_code' => '验证码错误或已过期']);
}
// 创建用户
$user = User::create([
'phone' => $request->phone,
'phone_verified_at' => now(),
// 其他字段...
]);
// 自动登录或重定向
auth()->login($user);
return redirect('/dashboard');
}
6. 验证状态检查
在用户模型中添加验证状态判断:
// app/Models/User.php
public function hasVerifiedPhone()
{
return !is_null($this->phone_verified_at);
}
public function markPhoneAsVerified()
{
$this->forceFill([
'phone_verified_at' => $this->freshTimestamp(),
])->save();
}
7. 测试用例
验证主要场景:
// tests/Feature/PhoneVerificationTest.php
public function test_phone_verification_flow()
{
// 测试无效格式
$response = $this->post('/register', [
'phone' => '1381234', // 错误格式
'sms_code' => '123456'
]);
$response->assertSessionHasErrors(['phone']);
// 测试验证码错误
$response = $this->post('/register', [
'phone' => '+8613812345678',
'sms_code' => 'wrong_code'
]);
$response->assertSessionHasErrors(['sms_code']);
// 测试成功注册
Cache::put('sms_code:+8613812345678', '123456');
$response = $this->post('/register', [
'phone' => '+8613812345678',
'sms_code' => '123456'
]);
$this->assertAuthenticated();
}
8. 前端展示优化
在 Blade 模板中添加验证状态提示:
<!-- resources/views/profile/phone-verify.blade.php -->
@if (auth()->user()->hasVerifiedPhone())
<div class="alert alert-success">
手机号已验证:{{ auth()->user()->phone }}
</div>
@else
<div class="alert alert-warning">
您的手机号未验证,<a href="{{ route('verify.phone') }}">立即验证</a>
</div>
@endif
扩展功能建议
- 防刷机制:限制同一IP/手机号的短信发送频率
// app/Http/Middleware/ThrottleSms.php
RateLimiter::for('sms', function (Request $request) {
return Limit::perMinute(1)->by($request->ip().'|'.$request->phone);
});
-
多渠道验证:支持语音验证码备用方案
-
国际化支持:根据国家代码自动匹配验证规则
通过以上实现,可以完成以下功能:
- ✅ 国际标准手机号格式验证
- ✅ 短信验证码发送与校验
- ✅ 防恶意请求保护
- ✅ 验证状态持久化记录
- ✅ 友好的错误提示系统