v1.0.51: 批次1架构筑基 - 1)版本迁移(common/migrate.php+schema_versions+natsort迁移文件);2)登录安全(bcrypt平滑迁移login/user_add/user_update旧sha1命中自动重写+system_login_attempts限流+session加固httponly/samesite/secure);3)CSRF Token(服务端checkCsrf+auth/csrf.php登录页取token+前端common.js/login.js统一携带X-CSRF-Token);4)统一基座common/Api.php并存量强制迁移全部70个endpoint(Api::boot按public/super/module/permissions分流,写接口强制checkAjax+checkCsrf,全局异常处理);5)前端收敛(common.js新增esc转义别名);6)REV-4硬数据来源渠道可编辑(hard_update+补全弹窗下拉);7)prepared卫生(soft_update.php修复+analysis.php表名白名单REV-9)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+7
-1
@@ -28,7 +28,7 @@ function httpPost(url, data, isFormData) {
|
||||
url: BASE_URL + url,
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-Token': window.CSRF_TOKEN || '' }
|
||||
};
|
||||
if (isFormData) {
|
||||
opts.data = data;
|
||||
@@ -128,6 +128,11 @@ function escHtml(str) {
|
||||
.replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/** esc 别名:与后端 esc() 同名,前端渲染动态内容统一转义 */
|
||||
function esc(str) {
|
||||
return escHtml(str);
|
||||
}
|
||||
|
||||
/** 手机号清洗:清除所有空白(含数字中间的空格),如 "138 1234 5678" -> "13812345678" */
|
||||
function cleanPhone(v) {
|
||||
return String(v == null ? '' : v).replace(/\s+/g, '');
|
||||
@@ -169,6 +174,7 @@ function checkPagePermission(key) {
|
||||
function initPage(activeKey, cb) {
|
||||
httpGet('auth/session.php').then(function (user) {
|
||||
window.CURRENT_USER = user;
|
||||
window.CSRF_TOKEN = user.csrf_token || ''; // 写接口统一携带 X-CSRF-Token
|
||||
try {
|
||||
renderMenu(activeKey, user.permissions || []);
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ var BASE_URL = '/api/';
|
||||
var PAGE_SIZE = 20;
|
||||
|
||||
/** 系统版本号(logo旁展示):修改代码后运行 tools/bump_version.php 自动递增 */
|
||||
var APP_VERSION = 'v1.0.50';
|
||||
var APP_VERSION = 'v1.0.51';
|
||||
|
||||
/** 页脚版权/备案信息(在 config.js 中修改) */
|
||||
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
||||
|
||||
@@ -333,6 +333,7 @@ $(function () {
|
||||
'<form id="cv-form" style="padding:16px 22px 4px;">' +
|
||||
'<div class="form-grid">' +
|
||||
' <div class="form-item"><label>碎片类型<span class="req">*</span></label>' + typeSel + '</div>' +
|
||||
' <div class="form-item"><label>来源渠道</label><select name="source_channel" id="cv-source"><option value="">-</option></select></div>' +
|
||||
' <div class="form-item" id="cv-f-company_name"><label>公司名称</label><input type="text" name="company_name" value="' + escHtml(d.company_name || '') + '"></div>' +
|
||||
' <div class="form-item" id="cv-f-person_name"><label>联系人</label><input type="text" name="person_name" value="' + escHtml(d.person_name || '') + '"></div>' +
|
||||
' <div class="form-item"><label>联系电话</label><input type="text" name="contact_phone" value="' + escHtml(d.contact_phone || '') + '"></div>' +
|
||||
@@ -363,6 +364,14 @@ $(function () {
|
||||
btn: false
|
||||
});
|
||||
$('#cv-platform').val(d.platform || '');
|
||||
// 来源渠道选项(与 source_channels 字典保持一致),并回显当前值
|
||||
httpGet('common/dicts.php', { type: 'all' }).then(function (dd) {
|
||||
var html = '<option value="">-</option>';
|
||||
(dd.source_channels || []).forEach(function (c) {
|
||||
html += '<option value="' + escHtml(c.name) + '">' + escHtml(c.name) + '</option>';
|
||||
});
|
||||
$('#cv-source').html(html).val(d.source_channel || '');
|
||||
}).catch(function () {});
|
||||
|
||||
function syncFields() {
|
||||
var t = $('#cv-type').val();
|
||||
|
||||
+14
-2
@@ -8,6 +8,14 @@ $(function () {
|
||||
$('#login-btn').prop('disabled', false);
|
||||
});
|
||||
|
||||
// 未登录场景:先从 auth/csrf.php 取会话绑定的 CSRF Token,写接口统一携带
|
||||
window.CSRF_TOKEN = '';
|
||||
$.getJSON(BASE_URL + 'auth/csrf.php').done(function (resp) {
|
||||
if (resp && resp.code === 0 && resp.data && resp.data.csrf_token) {
|
||||
window.CSRF_TOKEN = resp.data.csrf_token;
|
||||
}
|
||||
});
|
||||
|
||||
// 回车提交
|
||||
$('#password').on('keydown', function (e) {
|
||||
if (e.keyCode === 13) doLogin();
|
||||
@@ -15,6 +23,10 @@ $(function () {
|
||||
|
||||
$('#login-btn').on('click', doLogin);
|
||||
|
||||
function csrfHeaders() {
|
||||
return { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-Token': window.CSRF_TOKEN || '' };
|
||||
}
|
||||
|
||||
function doLogin() {
|
||||
var username = $.trim($('#username').val());
|
||||
var password = $('#password').val();
|
||||
@@ -27,7 +39,7 @@ $(function () {
|
||||
url: BASE_URL + 'auth/login.php',
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
headers: csrfHeaders(),
|
||||
data: { username: username, password: password, slider_token: sliderToken }
|
||||
}).done(function (resp) {
|
||||
if (resp.code === 0) {
|
||||
@@ -80,7 +92,7 @@ $(function () {
|
||||
url: BASE_URL + 'auth/forgot.php',
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
headers: csrfHeaders(),
|
||||
data: { username: username, contact_email: email }
|
||||
}).done(function (resp) {
|
||||
var color = resp.code === 0 ? '#27ae60;background:#eafaf1;border:1px solid #a9dfbf' : '#c0392b;background:#fdecea;border:1px solid #f5c6cb';
|
||||
|
||||
Reference in New Issue
Block a user