176 lines
5.8 KiB
PHP
176 lines
5.8 KiB
PHP
<?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();
|
||
}
|
||
}
|