PHP项目Laravel哈希密码验证过程

wen PHP项目 5

本文目录导读:

PHP项目Laravel哈希密码验证过程

  1. 密码哈希创建
  2. 密码验证过程
  3. 实际验证原理
  4. Laravel 认证流程完整示例
  5. 自定义验证逻辑
  6. 安全检查建议
  7. 配置选项
  8. 验证和重哈希
  9. 安全性最佳实践

在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')],
        ]);
    }
}

验证核心要点:

  1. Bcrypt 哈希包含盐值,相同密码会产生不同哈希
  2. 验证时使用password_verify()函数
  3. 使用hash_equals()进行时间安全的字符串比较
  4. 建议定期更新密码哈希的成本因子

抱歉,评论功能暂时关闭!