Files
Sdf1_login/admin_login.php
T

134 lines
6.3 KiB
PHP

<?php
// ★ 紧急加固(2026-09-29):第二层 IP 白名单 —— 登录页本身也只对白名单 IP 开放
require_once __DIR__ . '/security.php';
secGatePage();
// ★ 首跑顺序(2026-10-09):装库先于安全配置 —— install.php 还没跑完就先去装库。
// 引导模式下 secGatePage 已先行 302;这里兜住的是「本机 127.0.0.1 直连」与
// 「已引导但装库没做过」两种入口。secTokenUrl 在令牌未配置时原样返回
// install.php,配了令牌则带上 ?token= 过第三层门禁(与 admin.php 同款拼法)。
if (secInstallPending()) {
header('Location: ' . secTokenUrl('install.php'));
exit;
}
// ★ 首次部署:管理密码还是分发占位符 → 登录页是个死胡同(要密码,可密码还不存在)。
// 改投初始化页,让部署者先把自己的第一个密码设出来。
if (!secAdminPassConfigured()) {
header('Location: admin_2fa_setup.php');
exit;
}
// ★ 人机验证(2026-10-02):cgWidgetHtml() 渲染挂件,未配密钥时自动放行
require_once __DIR__ . '/captcha_guard.php';
// ★ 数据安全护栏横幅(2026-10-10):库被清空/指错库时,管理员一登录就能看到告警
require_once __DIR__ . '/db_guard.php';
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SDF1 - 管理后台登录</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0d1117; color: #e6edf3;
font-family: 'Segoe UI', system-ui, sans-serif;
display: flex; justify-content: center; align-items: center;
min-height: 100vh;
}
.login-box {
background: #161b22; border: 1px solid #30363d; border-radius: 12px;
padding: 40px; width: 380px; max-width: 90%;
}
.login-box h1 { text-align: center; color: #58a6ff; margin-bottom: 8px; font-size: 24px; }
.login-box .sub { text-align: center; color: #8b949e; font-size: 13px; margin-bottom: 28px; }
.field { margin-bottom: 16px; }
.field label { display: block; font-size: 13px; color: #8b949e; margin-bottom: 6px; }
.field input {
width: 100%; padding: 10px 14px; background: #0d1117; border: 1px solid #30363d;
border-radius: 6px; color: #e6edf3; font-size: 14px; outline: none; transition: border-color 0.2s;
}
.field input:focus { border-color: #58a6ff; }
.login-btn {
width: 100%; padding: 12px; background: #58a6ff; color: #fff; border: none;
border-radius: 6px; font-size: 15px; font-weight: 600; cursor: pointer;
transition: background 0.2s; margin-top: 8px;
}
.login-btn:hover { background: #79c0ff; }
.login-btn:disabled { background: #30363d; cursor: not-allowed; }
.error { color: #f85149; font-size: 13px; text-align: center; margin-top: 12px; display: none; }
.links { text-align: center; margin-top: 20px; }
.links a { color: #58a6ff; text-decoration: none; font-size: 13px; }
.links a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="login-box">
<h1>🔐 管理后台</h1>
<div class="sub">SDF1 插件管理系统</div>
<?php echo function_exists('dfGuardAdminBanner') ? dfGuardAdminBanner() : ''; ?>
<div class="field">
<label>管理密码</label>
<input type="password" id="password" placeholder="请输入管理密码" autofocus>
</div>
<?php echo cgWidgetHtml(); ?>
<button class="login-btn" id="loginBtn" onclick="doLogin()">登 录</button>
<div class="error" id="errorMsg"></div>
<div class="links"><a href="player.php">← 返回玩家商城</a></div>
</div>
<script>
document.getElementById('password').addEventListener('keydown', e => {
if (e.key === 'Enter') doLogin();
});
async function doLogin() {
const pw = document.getElementById('password').value;
if (!pw) return;
document.getElementById('loginBtn').disabled = true;
document.getElementById('loginBtn').textContent = '登录中...';
// ★ 人机验证(2026-10-02):CF 渲染完即有 token(基本无感);VA 没做会在这里拉起验证
let cap = {};
if (window.CaptchaGuard) {
try {
cap = await CaptchaGuard.payload();
} catch (e) {
document.getElementById('errorMsg').textContent = e.message || '请先完成人机验证';
document.getElementById('errorMsg').style.display = 'block';
document.getElementById('loginBtn').disabled = false;
document.getElementById('loginBtn').textContent = '登 录';
return;
}
}
try {
const res = await fetch('api/admin.php?action=login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(Object.assign({password: pw}, cap))
});
const data = await res.json();
if (data.success) {
// ★ 第三层:登录接口在密码正确后才下发入口令牌,用来拼 admin.php?token=
const tk = (data.data && data.data.token) ? ('?token=' + encodeURIComponent(data.data.token)) : '';
// ★ 第一层:启用二次验证时先进 6 位动态码验证页,通过后再进后台
if (data.data && data.data.need_2fa) {
window.location.href = 'admin_2fa.php';
} else {
window.location.href = 'admin.php' + tk;
}
} else {
document.getElementById('errorMsg').textContent = data.message;
document.getElementById('errorMsg').style.display = 'block';
}
} catch (e) {
document.getElementById('errorMsg').textContent = '网络错误';
document.getElementById('errorMsg').style.display = 'block';
}
document.getElementById('loginBtn').disabled = false;
document.getElementById('loginBtn').textContent = '登 录';
}
</script>
</body>
</html>