VPSHUB/app/auth.php
2026-08-25 00:07:26 +08:00

176 lines
5.8 KiB
PHP
Raw Permalink 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
/**
* VPS Hub 统一会话认证模块
*
* 功能:
* - 前端输入访问密码POST验证后建立 PHP 会话
* - 单次会话有效期 30 分钟(滑动过期)
* - 用户每次发起有效请求后自动刷新会话有效期
*
* 用法:
* require_once __DIR__ . '/app/auth.php';
* requireAuth(); // 未登录则展示登录页并终止脚本
* isLoggedIn(); // 返回当前会话是否有效
* attemptLogin($password); // 验证密码并建立会话
* logout(); // 手动退出登录
*/
// 会话有效期30 分钟(秒)
if (!defined('SESSION_LIFETIME')) {
define('SESSION_LIFETIME', 1800);
}
// 会话垃圾回收最大存活时间(需在 session_start 之前设置)
ini_set('session.gc_maxlifetime', SESSION_LIFETIME);
// 启动会话
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
/**
* 判断当前会话是否已通过认证
* 已登录且未超过 30 分钟无操作时,刷新会话有效期(滑动过期)
*
* @return bool 是否有效登录态
*/
function isLoggedIn() {
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
return false;
}
// 滑动过期判断:超过 30 分钟无任何请求则会话失效
if (!isset($_SESSION['last_activity']) || (time() - intval($_SESSION['last_activity'])) > SESSION_LIFETIME) {
// 销毁过期会话
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
}
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}
return false;
}
// 用户请求了新数据,刷新会话有效期
$_SESSION['last_activity'] = time();
return true;
}
/**
* 验证访问密码并建立登录会话
* 使用 hash_equals 进行时序安全比对,防止时序攻击
*
* @param string $password 用户提交的访问密码
* @return bool 是否登录成功
*/
function attemptLogin($password) {
if (!defined('API_PASS')) {
return false;
}
if (hash_equals(API_PASS, (string)$password)) {
// 会话固定攻击防护重新生成会话ID
session_regenerate_id(true);
$_SESSION['admin_logged_in'] = true;
$_SESSION['last_activity'] = time();
return true;
}
return false;
}
/**
* 强制要求登录认证
* 未认证时输出登录页面并终止当前脚本
* 已认证时直接返回,不影响后续流程
*
* @param string $redirect 登录成功后跳转的站内地址(可选,默认回到当前页面)
*/
function requireAuth($redirect = '') {
if (isLoggedIn()) {
return;
}
// 处理登录表单提交POST 携带 auth_password
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['auth_password'])) {
if (attemptLogin($_POST['auth_password'])) {
// 登录成功,跳转回目标页面
$target = !empty($_POST['redirect']) ? trim($_POST['redirect']) : $redirect;
// 仅允许站内跳转,防止开放重定向
if ($target && strpos($target, '://') === false && strpos($target, '\\') === false) {
header('Location: ' . $target);
} else {
header('Location: ' . $_SERVER['PHP_SELF']);
}
exit;
}
$error = '访问密码错误,请重试!';
}
// 构造登录成功后返回的地址(默认当前请求地址)
$currentPage = $_SERVER['PHP_SELF'];
$queryString = $_SERVER['QUERY_STRING'] ?? '';
$redirectTarget = !empty($redirect)
? $redirect
: ($queryString ? $currentPage . '?' . $queryString : $currentPage);
// 输出登录页面
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="./static/favicon.ico" type="image/x-icon">
<link href="./static/initial.css" rel="stylesheet" type="text/css">
<title>访问验证 - VPS Hub</title>
</head>
<body>
<div class="config-container">
<div class="config-header">
<h1>🔐 VPS Hub</h1>
<p>请输入访问密码以进入管理面板</p>
</div>
<?php if ($error): ?>
<div class="error-message">❌ <?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<form method="POST" action="<?php echo htmlspecialchars($currentPage); ?>">
<input type="hidden" name="redirect" value="<?php echo htmlspecialchars($redirectTarget); ?>">
<div class="form-group">
<label for="auth_password">访问密码</label>
<input type="password" id="auth_password" name="auth_password" required
placeholder="请输入访问密码" autofocus autocomplete="current-password">
</div>
<button type="submit" class="btn-submit">🔓 登录</button>
</form>
<div class="info-box" style="margin-top: 20px;">
💡 登录会话有效期 30 分钟,期间操作会自动续期;超过 30 分钟无操作需重新登录。
</div>
</div>
</body>
</html>
<?php
exit;
}
/**
* 退出登录并销毁会话
*/
function logout() {
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
}
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}
}