feat: 增加JWT安全认证并关闭接口文档

This commit is contained in:
2026-04-22 15:54:47 +08:00
parent ee50230907
commit 51084de7e1
4 changed files with 147 additions and 3 deletions
+15
View File
@@ -490,6 +490,21 @@
</div>
</div>
<!-- 登录对话框 -->
<div class="modal" id="loginModal">
<div class="modal-backdrop"></div>
<div class="modal-content">
<h3 id="loginTitle">系统登录</h3>
<p>由于安全原因,请先验证您的密码</p>
<div class="form-group" style="margin-top: 16px;">
<input type="password" id="loginPassword" class="form-input" placeholder="请输入密码以继续" onkeydown="if(event.key==='Enter') document.getElementById('loginBtn').click()">
</div>
<div class="modal-actions" style="margin-top: 24px;">
<button class="btn btn-primary" id="loginBtn" style="width: 100%;">登录</button>
</div>
</div>
</div>
<!-- 确认对话框 -->
<div class="modal" id="confirmModal">
<div class="modal-backdrop"></div>
+57
View File
@@ -114,10 +114,13 @@ async function api(endpoint, options = {}) {
// 如果没有指定 method 且没有 body,默认使用 GET
const method = options.method || (options.body ? 'POST' : 'GET');
const token = localStorage.getItem('token');
const fetchOptions = {
method: method,
headers: {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
...options.headers
},
...options
@@ -130,6 +133,12 @@ async function api(endpoint, options = {}) {
const response = await fetch(`/api${endpoint}`, fetchOptions);
if (response.status === 401 && endpoint !== '/login') {
localStorage.removeItem('token');
showLoginModal();
throw new Error('未授权或登录已过期,请重新登录');
}
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: response.statusText }));
throw new Error(error.detail || '请求失败');
@@ -138,6 +147,46 @@ async function api(endpoint, options = {}) {
return response.json();
}
function showLoginModal() {
const modal = $('#loginModal');
if (modal) {
modal.classList.add('active');
const loginBtn = $('#loginBtn');
const loginInput = $('#loginPassword');
loginInput.focus();
const handleLogin = async () => {
const password = loginInput.value;
if (!password) {
showToast('请输入密码', 'warning');
return;
}
try {
loginBtn.disabled = true;
loginBtn.textContent = '登录中...';
const res = await api('/login', {
method: 'POST',
body: JSON.stringify({ password })
});
if (res.success && res.token) {
localStorage.setItem('token', res.token);
modal.classList.remove('active');
showToast('登录成功', 'success');
loginInput.value = '';
window.location.reload();
}
} catch (err) {
showToast(err.message, 'error');
} finally {
loginBtn.disabled = false;
loginBtn.textContent = '登录';
}
};
loginBtn.onclick = handleLogin;
}
}
// ==================== 主题管理 ====================
@@ -629,6 +678,10 @@ class FileUploader {
} catch {
resolve({ success: true });
}
} else if (xhr.status === 401) {
localStorage.removeItem('token');
showLoginModal();
reject(new Error('未授权或登录已过期,请重新登录'));
} else {
reject(new Error('上传失败'));
}
@@ -641,6 +694,10 @@ class FileUploader {
// 开始上传
xhr.open('POST', '/api/upload');
const token = localStorage.getItem('token');
if (token) {
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
}
xhr.send(formData);
});