Laravel中Auth::attempt如何适配MSSQL的binary(13)类型字段
在Laravel中适配MSSQL binary(13)类型实现用户登录认证
问题背景
开发RF Online游戏网站时,MSSQL数据库中tbl_rfaccount表的id、password字段为binary(13)类型,无法修改该字段类型(否则会导致RF Launcher无法正常登录),但Laravel默认的Auth::attempt函数无法直接适配该类型,导致登录失败。
尝试过程
尝试1:直接调用Auth::attempt
直接传入用户输入的字符串调用Auth::attempt,始终返回登录失败:
$request->validate([ 'id' => 'required|min:4|max:11', 'password' => 'required|min:3|max:16' ]); try { if (Auth::attempt(['id' => $request->id, 'password' => $request->password])) { return to_route('dashboard'); } else { return to_route('login')->with('error', 'Oppss!! Username and password is wrong'); } } catch (\Exception $e) { dd($e); }
尝试2:手动查询并验证后调用Auth::attempt
先查询数据库将binary字段转换为字符串,确认输入值与转换后的值匹配,但传入数据库原始binary值调用Auth::attempt仍失败:
$request->validate([ 'id' => 'required|min:4|max:11', 'password' => 'required|min:3|max:16' ]); // 从tbl_rfaccount获取数据 $result = DB::selectOne('SELECT id, password FROM dbo.tbl_rfaccount WHERE id = CONVERT(varchar, ?)', [$request->id]); // 将binary结果转为字符串 $idString = str_replace("\x00", "", trim($result->id)); $passString = str_replace("\x00", "", trim($result->password)); try { // 验证输入值与转换后的值匹配 if ($request->id == $idString && $request->password == $passString) { // 使用数据库原始binary值调用Auth::attempt if (Auth::attempt(['id' => $result->id, 'password' => $result->password])) { return to_route('dashboard'); } else { return to_route('login')->with('error', 'Login failed, please try again'); } } else { return to_route('login')->with('error', 'Oppss!! Username and password is wrong'); } } catch (\Exception $e) { dd($e); }
Debug结果
通过dd()输出发现,数据库中的binary(13)字段是UTF-16LE编码的字符串,不足13字节时用\x00填充:
array:6 [▼ // app\Http\Controllers\Auth\LoginController.php:62 0 => "test6" 1 => "t\x00e\x00s\x00t\x006\x00\x00\x00\x00" 2 => "test6" 3 => "123" 4 => "1\x002\x003\x00\x00\x00\x00\x00\x00\x00\x00" 5 => "123" ]
解决方案
由于数据库中存储的是明文的UTF-16LE编码binary值,而非Laravel默认的哈希密码,因此需要手动处理字符串与binary的转换,并绕开Auth::attempt的默认密码验证逻辑:
步骤1:创建字符串转binary(13)的辅助函数
/** * 将字符串转换为符合MSSQL binary(13)要求的格式 * @param string $value 输入的字符串 * @return string 转换后的binary字符串 */ function stringToBinary13(string $value): string { // 将UTF-8字符串转换为UTF-16LE编码 $utf16le = mb_convert_encoding($value, 'UTF-16LE', 'UTF-8'); // 填充\x00到13字节长度 return str_pad($utf16le, 13, "\x00"); }
步骤2:手动完成认证与登录
在登录控制器中,将用户输入转换为对应binary格式后查询数据库,验证通过后直接调用Auth::login完成登录:
$request->validate([ 'id' => 'required|min:4|max:11', 'password' => 'required|min:3|max:16' ]); // 转换输入值为binary(13)格式 $binaryId = stringToBinary13($request->id); $binaryPassword = stringToBinary13($request->password); // 查询匹配的用户 $user = \App\Models\User::where('id', $binaryId)->first(); // 验证密码并登录 if ($user && $user->password === $binaryPassword) { Auth::login($user); return to_route('dashboard'); } return to_route('login')->with('error', '用户名或密码错误');
补充:为用户模型添加字段转换(可选)
如果需要在其他场景中方便地使用字符串格式的id和password,可以在User模型中添加访问器,自动将binary字段转换为字符串:
namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { protected $table = 'tbl_rfaccount'; protected $primaryKey = 'id'; public $timestamps = false; // 将binary id转换为字符串 public function getIdAttribute($value) { return str_replace("\x00", "", trim(mb_convert_encoding($value, 'UTF-8', 'UTF-16LE'))); } // 将binary password转换为字符串 public function getPasswordAttribute($value) { return str_replace("\x00", "", trim(mb_convert_encoding($value, 'UTF-8', 'UTF-16LE'))); } }
内容的提问来源于stack exchange,提问作者Willy Sudwihartono
相关产品推荐
相关产品推荐

