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:
qianqiuwanzi
2026-09-17 14:48:17 +08:00
parent 69fd4246c2
commit c86f08c325
91 changed files with 1072 additions and 463 deletions
+2 -4
View File
@@ -4,11 +4,9 @@
* 仅超级管理员可见,默认查询近1个月
* 参数:page / limit / keyword / action / module / username / start_date / end_date
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['super' => true]);
requireSuperAdmin();
[$page, $limit] = pageParams();
$keyword = trim($_REQUEST['keyword'] ?? '');
+2 -6
View File
@@ -3,13 +3,9 @@
* 新增角色接口(含权限分配) POST /api/system/role_add.php
* 入参:role_name / permissions(JSON数组,菜单标识)/ is_active
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['module' => 'system']);
checkAjax();
checkPermission('system');
$roleName = trim($_POST['role_name'] ?? '');
$perms = json_decode($_POST['permissions'] ?? '[]', true);
+2 -6
View File
@@ -4,13 +4,9 @@
* 入参:id(或 ids 逗号分隔批量)
* 保护:不允许删除内置超级管理员角色(id=1)及仍被用户使用的角色
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['module' => 'system']);
checkAjax();
checkPermission('system');
$id = (int)($_POST['id'] ?? 0);
$ids = trim($_POST['ids'] ?? '');
+2 -4
View File
@@ -3,11 +3,9 @@
* 角色列表接口 GET/POST /api/system/role_list.php
* 参数:page / limit / keyword / is_active
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['module' => 'system']);
checkPermission('system');
[$page, $limit] = pageParams();
$keyword = trim($_REQUEST['keyword'] ?? '');
+2 -6
View File
@@ -3,13 +3,9 @@
* 编辑角色接口(含权限分配) POST /api/system/role_update.php
* 入参:id / role_name / permissions(JSON数组)/ is_active
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['module' => 'system']);
checkAjax();
checkPermission('system');
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
+5 -11
View File
@@ -3,13 +3,9 @@
* 新增用户接口 POST /api/system/user_add.php
* 入参:username / password / real_name / role_id / is_active
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/Api.php';
checkAjax();
checkPermission('system');
$pdo = Api::boot(['module' => 'system']);
$username = trim($_POST['username'] ?? '');
$password = (string)($_POST['password'] ?? '');
@@ -20,15 +16,13 @@ $isActive = isset($_POST['is_active']) ? ((int)$_POST['is_active'] ? 1 : 0) : 1;
if ($username === '' || !preg_match('/^[a-zA-Z0-9_]{3,50}$/', $username)) {
Response::error('账号需为3-50位字母/数字/下划线', 400);
}
if (strlen($password) < 6) {
Response::error('密码长度不能少于6位', 400);
if (strlen($password) < 8 || !preg_match('/[a-zA-Z]/', $password) || !preg_match('/\d/', $password)) {
Response::error('密码需不少于8位,且同时包含字母和数字', 400);
}
if ($roleId <= 0) {
Response::error('请选择角色', 400);
}
$pdo = DB::getInstance()->getPdo();
$chk = $pdo->prepare("SELECT COUNT(*) FROM system_users WHERE username = ?");
$chk->execute([$username]);
if ((int)$chk->fetchColumn() > 0) {
@@ -45,7 +39,7 @@ $stmt = $pdo->prepare(
"INSERT INTO system_users (username, password, real_name, role_id, is_active)
VALUES (?, ?, ?, ?, ?)"
);
$stmt->execute([$username, sha1($password), $realName !== '' ? $realName : null, $roleId, $isActive]);
$stmt->execute([$username, password_hash($password, PASSWORD_DEFAULT), $realName !== '' ? $realName : null, $roleId, $isActive]);
$newId = (int)$pdo->lastInsertId();
logCurrent('add', 'system', 'system_users', $newId, ['username' => $username, 'role_id' => $roleId, 'is_active' => $isActive]);
+2 -6
View File
@@ -4,13 +4,9 @@
* 入参:id(或 ids 逗号分隔批量)
* 保护:不允许删除内置管理员(id=1)
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['module' => 'system']);
checkAjax();
checkPermission('system');
$id = (int)($_POST['id'] ?? 0);
$ids = trim($_POST['ids'] ?? '');
+2 -4
View File
@@ -3,11 +3,9 @@
* 用户列表接口 GET/POST /api/system/user_list.php
* 参数:page / limit / keyword / role_id / is_active
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/Api.php';
$pdo = Api::boot(['module' => 'system']);
checkPermission('system');
[$page, $limit] = pageParams();
$keyword = trim($_REQUEST['keyword'] ?? '');
+5 -11
View File
@@ -3,20 +3,14 @@
* 编辑用户接口(含角色分配) POST /api/system/user_update.php
* 入参:id / real_name / role_id / is_active / password(可选,留空不改密码)
*/
require_once __DIR__ . '/../common/db.php';
require_once __DIR__ . '/../common/response.php';
require_once __DIR__ . '/../common/auth.php';
require_once __DIR__ . '/../common/logger.php';
require_once __DIR__ . '/../common/Api.php';
checkAjax();
checkPermission('system');
$pdo = Api::boot(['module' => 'system']);
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
Response::error('参数错误', 400);
}
$pdo = DB::getInstance()->getPdo();
$check = $pdo->prepare("SELECT * FROM system_users WHERE id = ?");
$check->execute([$id]);
$old = $check->fetch();
@@ -52,11 +46,11 @@ if (isset($_POST['is_active'])) {
}
$password = (string)($_POST['password'] ?? '');
if ($password !== '') {
if (strlen($password) < 6) {
Response::error('密码长度不能少于6位', 400);
if (strlen($password) < 8 || !preg_match('/[a-zA-Z]/', $password) || !preg_match('/\d/', $password)) {
Response::error('密码需不少于8位,且同时包含字母和数字', 400);
}
$sets[] = 'password = ?';
$params[] = sha1($password);
$params[] = password_hash($password, PASSWORD_DEFAULT);
}
if (!$sets) {