登录支持用户名,与手机号并存 (#2)
原来 Auth::attempt 只按 phone_hash 查,用户名永远登不进去。 家人账号常常不填手机号,演示场景下用名字登录更自然。 - 先按手机号 hash 精确查(唯一),未命中再按 name 查 - 重名命中多条一律拒绝,绝不取第一条(否则 A 的密码能登进 B 的账号) - 表单 label 改「手机号 / 用户名」,错误文案改「账号或密码错误」 验证:逻辑单测 10/10(含重名拒绝);反向验证改坏判断即变红 (#2)
This commit is contained in:
+31
-8
@@ -60,21 +60,44 @@ class Auth
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过手机号 + 密码尝试登录
|
||||
* 尝试登录。账号可以是**手机号**,也可以是**姓名**。
|
||||
*
|
||||
* 为什么两种都收:安装向导创建的管理员填的是手机号,而家人账号常常不填手机号
|
||||
* (只有名字),演示/内网场景下用名字登录更自然。
|
||||
*
|
||||
* 顺序:先按手机号 hash 精确匹配(唯一,不会歧义),再按姓名匹配。
|
||||
* 姓名可能重名 ⇒ **命中多条一律判失败**,绝不「取第一条」——
|
||||
* 那等于让 A 用密码登进了 B 的账号,是最坏的一种 bug。
|
||||
*
|
||||
* @return array|null 成功返回用户数组,失败返回 null
|
||||
*/
|
||||
public static function attempt(string $phone, string $password): ?array
|
||||
public static function attempt(string $account, string $password): ?array
|
||||
{
|
||||
$phone = trim($phone);
|
||||
if ($phone === '' || $password === '') return null;
|
||||
$account = trim($account);
|
||||
if ($account === '' || $password === '') return null;
|
||||
|
||||
$phoneHash = hash('sha256', $phone);
|
||||
$stmt = Database::get()->prepare(
|
||||
$db = Database::get();
|
||||
|
||||
// ① 手机号:走 sha256 精确查(唯一,不会歧义)
|
||||
$stmt = $db->prepare(
|
||||
'SELECT * FROM users WHERE phone_hash = ? AND role IN ("admin","user") AND status = "active" LIMIT 1'
|
||||
);
|
||||
$stmt->execute([$phoneHash]);
|
||||
$stmt->execute([hash('sha256', $account)]);
|
||||
$user = $stmt->fetch();
|
||||
if (!$user) return null;
|
||||
|
||||
// ② 手机号没命中,再按姓名查
|
||||
if (!$user) {
|
||||
$stmt = $db->prepare(
|
||||
'SELECT * FROM users WHERE name = ? AND role IN ("admin","user") AND status = "active"'
|
||||
);
|
||||
$stmt->execute([$account]);
|
||||
$rows = $stmt->fetchAll();
|
||||
|
||||
// 重名 → 拒绝,不做猜测
|
||||
if (count($rows) !== 1) return null;
|
||||
$user = $rows[0];
|
||||
}
|
||||
|
||||
if (!password_verify($password, (string)($user['password_hash'] ?? ''))) return null;
|
||||
|
||||
return $user;
|
||||
|
||||
@@ -24,7 +24,7 @@ class AuthController
|
||||
|
||||
$user = Auth::attempt($phone, $password);
|
||||
if (!$user) {
|
||||
flash('error', '手机号或密码错误');
|
||||
flash('error', '账号或密码错误');
|
||||
redirect('auth/login');
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<p class="muted">请登录后继续</p>
|
||||
|
||||
<div class="form-row">
|
||||
<label>手机号</label>
|
||||
<input type="text" name="phone" required autofocus placeholder="请输入手机号">
|
||||
<label>手机号 / 用户名</label>
|
||||
<input type="text" name="phone" required autofocus placeholder="手机号或用户名">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>密码</label>
|
||||
|
||||
Reference in New Issue
Block a user