如何在Laravel中使用自定义HASHBYTES('md5')实现登录?
基于HASHBYTES('MD5')实现Laravel登录的优化方案
方案一:重写User模型的密码验证方法
Laravel的认证系统会自动调用模型的validateCredentials方法做密码校验,我们直接重写这个方法,适配HASHBYTES('MD5')的规则即可,无需重复查询数据库:
- 修改User模型代码:
namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Support\Facades\DB; class User extends Authenticatable { // 保留原有模型属性与方法 /** * 验证输入密码与数据库存储的哈希是否匹配 * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param array $credentials * @return bool */ public function validateCredentials(AuthenticatableContract $user, array $credentials) { $inputPassword = $credentials['PASSWORD']; // 调用数据库HASHBYTES生成哈希,与数据库存储值对比 $computedHash = DB::selectOne("SELECT HASHBYTES('MD5', ?) AS hashed", [$inputPassword])->hashed; // 使用hash_equals避免时序攻击 return hash_equals($user->PASSWORD, $computedHash); } }
- 简化登录控制器代码:
直接用Laravel Auth的attempt方法完成认证,不用自己写两次查询:
public function masuk(Request $request) { $this->validate($request, [ 'USERNAME_ACC' => 'required', 'PASSWORD' => 'required' ]); $credentials = [ 'USERNAME_ACC' => $request->USERNAME_ACC, 'PASSWORD' => $request->PASSWORD // 直接传明文,模型会自动处理哈希验证 ]; if (Auth::guard('web')->attempt($credentials)) { return redirect()->intended('admin'); } return redirect('/login')->with('gagal', 'Username or Password Not Register'); }
方案二:自定义哈希驱动(适配全场景)
如果需要在用户注册、密码重置等场景也统一使用HASHBYTES('MD5'),可以自定义哈希驱动,更符合Laravel的生态规范:
- 创建自定义哈希驱动类
在app/Providers/Hash目录下新建HashBytesMd5Driver.php(目录不存在则手动创建):
namespace App\Providers\Hash; use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Support\Facades\DB; class HashBytesMd5Driver implements Hasher { /** * 生成密码哈希 * * @param string $value * @param array $options * @return string */ public function make($value, array $options = []) { return DB::selectOne("SELECT HASHBYTES('MD5', ?) AS hashed", [$value])->hashed; } /** * 验证明文与哈希是否匹配 * * @param string $hashedValue * @param string $value * @param array $options * @return bool */ public function check($value, $hashedValue, array $options = []) { if (empty($hashedValue)) return false; $computedHash = $this->make($value); return hash_equals($hashedValue, $computedHash); } /** * 检查哈希是否需要重新生成(MD5无需重哈希,返回false) * * @param string $hashedValue * @param array $options * @return bool */ public function needsRehash($hashedValue, array $options = []) { return false; } }
- 注册自定义驱动
修改config/hashing.php配置文件,添加自定义驱动:
return [ 'default' => env('HASH_DRIVER', 'hashbytesmd5'), // 可设为默认驱动,或保持原驱动按需指定 'drivers' => [ // 保留原有驱动(bcrypt/argon2等) 'hashbytesmd5' => [ 'driver' => 'hashbytesmd5', ], ], ];
- 在服务提供者中绑定驱动
修改app/Providers/AppServiceProvider.php的boot方法:
namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Hashing\HashManager; use App\Providers\Hash\HashBytesMd5Driver; class AppServiceProvider extends ServiceProvider { public function boot() { $this->app->make(HashManager::class)->extend('hashbytesmd5', function ($app) { return new HashBytesMd5Driver(); }); } }
- 实际使用
- 注册用户时生成哈希:
$user = new User(); $user->USERNAME_ACC = $request->USERNAME_ACC; $user->PASSWORD = \Hash::driver('hashbytesmd5')->make($request->PASSWORD); $user->save();
- 登录验证(也可复用方案一的模型重写方式,或直接在控制器中调用):
public function masuk(Request $request) { $this->validate($request, [ 'USERNAME_ACC' => 'required', 'PASSWORD' => 'required' ]); $user = User::where('USERNAME_ACC', $request->USERNAME_ACC)->first(); if ($user && \Hash::driver('hashbytesmd5')->check($request->PASSWORD, $user->PASSWORD)) { Auth::guard('web')->login($user); return redirect()->intended('admin'); } return redirect('/login')->with('gagal', 'Username or Password Not Register'); }
关键注意事项
- 数据库
PASSWORD字段类型建议设为VARBINARY(16)或BINARY(16),因为HASHBYTES('MD5')生成的是二进制哈希值,用字符串类型存储可能出现编码问题导致验证失败。 - MD5属于弱哈希算法,安全性极低,若为新项目强烈建议使用Laravel默认的Bcrypt/Argon2算法;若为遗留系统必须使用MD5,建议添加固定盐值(比如在HASHBYTES中拼接用户ID或固定字符串)提升安全性。
内容的提问来源于stack exchange,提问作者Hasun
相关产品推荐
相关产品推荐

