本文目录导读:

在Laravel中,密码哈希和验证是通过Illuminate\Contracts\Hashing\Hasher接口实现的,默认使用BcryptHasher,以下是完整的验证过程:
密码哈希创建
Bcrypt 哈希过程
// Laravel 使用 bcrypt 算法生成密码哈希
$hashedPassword = Hash::make('password123');
// 输出示例
// $2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi
哈希格式解析:
$2y$- 算法标识(bcrypt)10- 成本因子(cost factor,默认为10)- 后续字符 - 22字符的盐值 + 31字符的哈希值
密码验证过程
完整验证流程
步骤1:获取用户记录
$user = User::where('email', $request->email)->first();
步骤2:调用验证方法
// 方式1:使用 Auth 门面
if (Auth::attempt(['email' => $email, 'password' => $password])) {
// 认证成功
}
// 方式2:手动验证
if (Hash::check($request->password, $user->password)) {
// 密码正确
}
// 方式3:使用 Auth::validate()
$credentials = ['email' => $email, 'password' => $password];
if (Auth::validate($credentials)) {
// 验证成功
}
步骤3:内部验证机制
// BcryptHasher 的 check 方法
public function check($value, $hashedValue, array $options = [])
{
if (strlen($hashedValue) === 0) {
return false;
}
return password_verify($value, $hashedValue);
}
实际验证原理
Bcrypt 验证过程
// PHP 内置的 password_verify 函数
public function verifyPassword($plainPassword, $hashedPassword) {
/*
* 1. 从哈希中提取盐值和成本因子
* 2. 使用相同的盐值和成本因子重新计算哈希
* 3. 比较结果
*/
return password_verify($plainPassword, $hashedPassword);
}
详细验证步骤
// 1. 提取哈希中的盐值 $hash = '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi'; $salt = substr($hash, 0, 29); // 包含算法、成本因子和盐值 // 2. 使用相同参数重新哈希 $newHash = crypt($plainPassword, $salt); // 3. 比较结果 $isValid = hash_equals($newHash, $hash);
Laravel 认证流程完整示例
// LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
class LoginController extends Controller
{
/**
* 处理登录请求
*/
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
// 认证过程
if (Auth::attempt($credentials, $request->boolean('remember'))) {
// 认证成功
$request->session()->regenerate();
// 获取当前用户
$user = Auth::user();
// 验证用户状态
if (!$user->is_active) {
Auth::logout();
return back()->withErrors([
'email' => '账号已被禁用',
]);
}
return redirect()->intended('dashboard');
}
// 认证失败
return back()->withErrors([
'email' => '提供的凭证与记录不匹配',
])->onlyInput('email');
}
}
自定义验证逻辑
<?php
namespace App\Services;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthenticationService
{
/**
* 自定义密码验证
*/
public function authenticateUser(array $credentials): array
{
$user = User::where('email', $credentials['email'])->first();
// 用户存在且密码验证通过
if (!$user || !Hash::check($credentials['password'], $user->password)) {
return ['success' => false, 'message' => '无效的登录凭据'];
}
// 检查额外条件
if (!$user->is_active) {
return ['success' => false, 'message' => '账号已停用'];
}
// 记录最近登录时间
$user->update([
'last_login_at' => now(),
'last_login_ip' => request()->ip(),
]);
// 更新密码哈希(如果成本因子改变)
if (Hash::needsRehash($user->password)) {
$user->update([
'password' => Hash::make($credentials['password'])
]);
}
return ['success' => true, 'user' => $user];
}
}
安全检查建议
// 密码强度验证
public function validatePasswordStrength($password) {
// 至少8个字符
if (strlen($password) < 8) {
return false;
}
// 包含字母、数字、特殊字符
if (!preg_match('/[A-Z]/', $password) ||
!preg_match('/[a-z]/', $password) ||
!preg_match('/[0-9]/', $password) ||
!preg_match('/[^A-Za-z0-9]/', $password)) {
return false;
}
return true;
}
// 防暴力破解
public function throttleLoginAttempts(Request $request) {
$key = 'login_attempts:' . $request->ip();
$attempts = Cache::get($key, 0);
$attempts++;
Cache::put($key, $attempts, now()->addMinutes(15));
if ($attempts > 5) {
abort(429, '尝试次数过多,请稍后再试');
}
}
配置选项
在config/hashing.php中配置:
// config/hashing.php
return [
'driver' => 'bcrypt',
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 10),
'verify' => false, // 是否需要验证算法
],
'argon' => [
'memory' => 1024,
'threads' => 2,
'time' => 2,
'verify' => false,
],
];
验证和重哈希
// 登录时检查是否需要重新哈希
public function checkAndRehash($user, $plainPassword) {
if (Hash::needsRehash($user->password)) {
// 重新计算哈希
$user->forceFill([
'password' => Hash::make($plainPassword),
])->save();
}
}
安全性最佳实践
// 使用安全的登录流程
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
use AuthenticatesUsers;
// 登录尝试次数限制
protected $maxAttempts = 5;
// 锁定时间(分钟)
protected $decayMinutes = 15;
// 登录失败后的处理
protected function sendFailedLoginResponse(Request $request)
{
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
}
验证核心要点:
- Bcrypt 哈希包含盐值,相同密码会产生不同哈希
- 验证时使用
password_verify()函数 - 使用
hash_equals()进行时间安全的字符串比较 - 建议定期更新密码哈希的成本因子