110 lines
3.2 KiB
PHP
110 lines
3.2 KiB
PHP
<?php
|
||
/**
|
||
* auth.php —— 后台登录会话工具
|
||
* 密码存储算法(按需求):md5(base64(明文))
|
||
*/
|
||
require_once __DIR__ . '/db.php';
|
||
|
||
if (session_status() === PHP_SESSION_NONE) {
|
||
session_start();
|
||
}
|
||
|
||
/** 是否已登录 */
|
||
function is_logged_in(): bool
|
||
{
|
||
return !empty($_SESSION['admin']);
|
||
}
|
||
|
||
/** 未登录则跳转登录页(后台各页顶部调用) */
|
||
function require_login(string $loginPage = 'login.php'): void
|
||
{
|
||
if (!is_logged_in()) {
|
||
header('Location: ' . $loginPage);
|
||
exit;
|
||
}
|
||
}
|
||
|
||
/** 计算需求规定的密码散列:先 base64 再 md5 */
|
||
function hp_password_hash(string $plain): string
|
||
{
|
||
return md5(base64_encode($plain));
|
||
}
|
||
|
||
/** 验证用户 */
|
||
function hp_verify_user(string $username, string $plain): bool
|
||
{
|
||
$st = db()->prepare('SELECT password_hash FROM users WHERE username = ? LIMIT 1');
|
||
$st->execute([$username]);
|
||
$row = $st->fetch();
|
||
if (!$row) {
|
||
return false;
|
||
}
|
||
return hash_equals($row['password_hash'], hp_password_hash($plain));
|
||
}
|
||
|
||
/** 执行登录(成功返回 true;成功/失败均写入登录日志与设备信息) */
|
||
function hp_login(string $username, string $plain, string $deviceClient = ''): bool
|
||
{
|
||
$uaRaw = (string) ($_SERVER['HTTP_USER_AGENT'] ?? '');
|
||
if (hp_verify_user($username, $plain)) {
|
||
session_regenerate_id(true);
|
||
$_SESSION['admin'] = $username;
|
||
// 记录本次登录信息(供后台“登录信息”卡片展示)
|
||
$_SESSION['login_at'] = date('Y-m-d H:i:s');
|
||
$_SESSION['login_ip'] = client_ip();
|
||
$_SESSION['login_ua'] = login_ua_brief($uaRaw);
|
||
$_SESSION['login_device'] = login_device_brief($deviceClient, $uaRaw);
|
||
login_log_write($username, 'ok', $deviceClient);
|
||
return true;
|
||
}
|
||
login_log_write($username, 'fail', $deviceClient);
|
||
return false;
|
||
}
|
||
|
||
/** 修改当前用户密码 */
|
||
function hp_change_password(string $oldPlain, string $newPlain): array
|
||
{
|
||
if (!is_logged_in()) {
|
||
return [false, '未登录'];
|
||
}
|
||
if (!hp_verify_user((string) $_SESSION['admin'], $oldPlain)) {
|
||
return [false, '原密码不正确'];
|
||
}
|
||
if (strlen($newPlain) < 6) {
|
||
return [false, '新密码至少 6 位'];
|
||
}
|
||
$st = db()->prepare('UPDATE users SET password_hash = ? WHERE username = ?');
|
||
$st->execute([hp_password_hash($newPlain), (string) $_SESSION['admin']]);
|
||
return [true, '密码修改成功'];
|
||
}
|
||
|
||
/** 退出登录 */
|
||
function hp_logout(): void
|
||
{
|
||
$_SESSION = [];
|
||
if (ini_get('session.use_cookies')) {
|
||
$p = session_get_cookie_params();
|
||
setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
|
||
}
|
||
session_destroy();
|
||
}
|
||
|
||
/** CSRF token */
|
||
function csrf_token(): string
|
||
{
|
||
if (empty($_SESSION['csrf'])) {
|
||
$_SESSION['csrf'] = bin2hex(random_bytes(16));
|
||
}
|
||
return (string) $_SESSION['csrf'];
|
||
}
|
||
|
||
function csrf_field(): string
|
||
{
|
||
return '<input type="hidden" name="csrf" value="' . htmlspecialchars(csrf_token(), ENT_QUOTES, 'UTF-8') . '">';
|
||
}
|
||
|
||
function csrf_verify(): bool
|
||
{
|
||
return isset($_POST['csrf']) && is_string($_POST['csrf']) && hash_equals(csrf_token(), $_POST['csrf']);
|
||
}
|