Files
superlink/api/auth/login.php
T

87 lines
3.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 登录接口 POST /api/auth/login.php
* 入参:username / password / slider_token(滑块验证通过后的token)
* 出参(成功):{ code:0, msg:'登录成功', data:{ real_name, role_name, permissions } }
*
* 批次1 安全加固(REV-1/REV-8):
* - 密码 bcrypt(password_hash)优先校验;存量 sha1 用户登录命中后平滑升级
* - 登录限流(15分钟内同 IP+账号失败≥5 次锁定)
* - 登录成功后 session_regenerate_id 防会话固定
* - 经 Api::boot 统一走 CSRF + 安全 Session
*/
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['public' => true]);
$username = trim($_POST['username'] ?? '');
$password = (string)($_POST['password'] ?? '');
$sliderToken = trim($_POST['slider_token'] ?? '');
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
// 滑块验证:前端 slider.js 拖动通过后生成的 token,服务端校验非空且格式合法
if ($sliderToken === '' || strlen($sliderToken) < 10) {
Response::error('请先完成滑块验证');
}
if ($username === '' || $password === '') {
Response::error('请输入账号和密码');
}
// 登录限流(fail-open)
checkLoginThrottle($username, $ip);
$stmt = $pdo->prepare(
"SELECT u.*, r.role_name, r.permissions
FROM system_users u
LEFT JOIN system_roles r ON r.id = u.role_id
WHERE u.username = ? LIMIT 1"
);
$stmt->execute([$username]);
$user = $stmt->fetch();
// 密码校验:bcrypt 优先;旧版无盐 sha1 校验通过后即时平滑升级为 bcrypt
$authOk = false;
if ($user) {
if (password_verify($password, (string)$user['password'])) {
$authOk = true;
} elseif (hash_equals((string)$user['password'], sha1($password))) {
$authOk = true;
$pdo->prepare("UPDATE system_users SET password = ? WHERE id = ?")
->execute([password_hash($password, PASSWORD_DEFAULT), (int)$user['id']]);
}
}
if (!$authOk) {
recordLoginAttempt($username, $ip, false);
Response::error('账号或密码错误');
}
if ((int)$user['is_active'] !== 1) {
recordLoginAttempt($username, $ip, false);
Response::error('账号已被禁用,请联系管理员');
}
recordLoginAttempt($username, $ip, true);
// 更新最后登录时间/IP
$upd = $pdo->prepare("UPDATE system_users SET last_login_time = NOW(), last_login_ip = ? WHERE id = ?");
$upd->execute([$ip, $user['id']]);
// 会话固定防护:登录成功后重新生成 session id(保留已有 CSRF Token)
session_regenerate_id(true);
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['real_name'] = $user['real_name'] ?? $user['username'];
$_SESSION['role_id'] = (int)$user['role_id'];
$_SESSION['role_name'] = $user['role_name'] ?? '';
$_SESSION['permissions'] = json_decode($user['permissions'] ?? '[]', true) ?: [];
// 操作日志
logAction((int)$user['id'], $user['username'], 'login', 'auth', 'system_users', (int)$user['id'], ['ip' => $ip]);
Response::success([
'real_name' => $_SESSION['real_name'],
'role_name' => $_SESSION['role_name'],
'permissions' => $_SESSION['permissions'],
'csrf_token' => csrfToken(),
], '登录成功');