初始化

This commit is contained in:
2026-08-08 18:28:49 +08:00
parent 9bef4420e0
commit f082037a6e
854 changed files with 217171 additions and 11 deletions
+192
View File
@@ -0,0 +1,192 @@
<?php
/**
* 一次性弱密码重置脚本(P1:管理员弱密码 123456)
*
* 用法(在服务器上通过 CLI 执行,不要在浏览器访问):
* php fix_db_password.php # 自动探测员工表
* php fix_db_password.php --table=jp_employee # 手动指定表名
*
* 作用:扫描员工表中仍在使用常见弱密码(如 123456)的账号,
* 将其重置为自动生成的强随机密码(≥16 位,含大小写+数字+特殊字符),
* 并打印新密码,请妥善保存并告知对应员工。
*
* 注意:本文件已被 .htaccess 禁止通过 Web 访问,仅可 CLI 执行。
* 执行完成后建议直接删除本文件。
*/
// 加载配置(与 index.php 相同的 .env 读取逻辑)
$envFile = __DIR__ . '/.env';
if (file_exists($envFile)) {
foreach (file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
$line = trim($line);
if ($line === '' || $line[0] === '#' || strpos($line, '=') === false) continue;
list($k, $v) = explode('=', $line, 2);
if (!array_key_exists(trim($k), $_ENV)) $_ENV[trim($k)] = trim($v);
}
}
define('DB_HOST', $_ENV['DB_HOST'] ?? 'localhost');
define('DB_NAME', $_ENV['DB_NAME'] ?? 'mes');
define('DB_USER', $_ENV['DB_USER'] ?? 'root');
define('DB_PASS', $_ENV['DB_PASS'] ?? '');
try {
$pdo = new PDO(
sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', DB_HOST, DB_NAME),
DB_USER, DB_PASS,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
} catch (PDOException $e) {
exit("数据库连接失败:{$e->getMessage()}\n");
}
// 解析命令行参数(--table=xxx
$forcedTable = '';
foreach ($argv as $a) {
if (strpos($a, '--table=') === 0) {
$forcedTable = substr($a, strlen('--table='));
}
}
// 常见弱密码字典
$weakList = ['123456','12345678','123456789','password','admin123','qwerty','111111','abc123','123123','root'];
// ---- 自动探测员工表与字段 ----
function getAllTables($pdo)
{
return $pdo->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN);
}
function getColumns($pdo, $table)
{
return $pdo->query("SHOW COLUMNS FROM `{$table}`")->fetchAll(PDO::FETCH_COLUMN);
}
// 从字段列表中挑选首个命中的字段名
function pickField(array $cols, array $candidates)
{
foreach ($candidates as $c) {
if (in_array($c, $cols, true)) return $c;
}
return '';
}
function chooseEmployeeTable($pdo, $forced)
{
$tables = getAllTables($pdo);
if ($forced !== '') {
if (!in_array($forced, $tables, true)) {
exit("指定的表 `{$forced}` 不存在。可用表:\n " . implode("\n ", $tables) . "\n");
}
return $forced;
}
// 候选评分:优先名称含 employee,其次含 user/admin/staff,且字段需含密码字段
$scored = [];
foreach ($tables as $t) {
$cols = getColumns($pdo, $t);
$pwdField = pickField($cols, ['password','pwd','passwd','user_pass','user_password','hash','pass']);
if ($pwdField === '') continue; // 无密码字段,不是用户表
$score = 0;
if (stripos($t, 'employee') !== false) $score += 100;
if (stripos($t, 'admin') !== false) $score += 60;
if (stripos($t, 'user') !== false) $score += 50;
if (stripos($t, 'staff') !== false) $score += 40;
if (stripos($t, 'member') !== false) $score += 30;
if (stripos($t, 'jp_') === 0) $score += 10; // 业务前缀表
if ($score > 0) {
$scored[] = ['table' => $t, 'score' => $score, 'cols' => $cols];
}
}
if (empty($scored)) {
exit("未在数据库中找到含密码字段的用户表。所有表:\n " . implode("\n ", $tables) . "\n请使用 --table=表名 手动指定。\n");
}
// 分数最高者唯一则直接用;否则列出候选请用户指定
usort($scored, fn($a, $b) => $b['score'] <=> $a['score']);
$top = $scored[0]['score'];
$topCandidates = array_values(array_filter($scored, fn($x) => $x['score'] === $top));
if (count($topCandidates) === 1) {
return $topCandidates[0]['table'];
}
echo "检测到多个候选用户表,请使用 --table= 指定其中一个:\n";
foreach ($scored as $c) {
echo " {$c['table']} (score={$c['score']})\n";
}
exit;
}
$table = chooseEmployeeTable($pdo, $forcedTable);
$cols = getColumns($pdo, $table);
// 字段映射(自适应不同命名)
$idField = pickField($cols, ['id']) ?: 'id';
$pwdField = pickField($cols, ['password','pwd','passwd','user_pass','user_password','hash','pass']);
$empNoField = pickField($cols, ['emp_no','username','user_no','job_no','work_no','account','login_name','user_name','login_id']);
$nameField = pickField($cols, ['emp_name','name','user_name','real_name','nickname','truename']);
$roleField = pickField($cols, ['role','position','job_title','type','user_type']);
if ($pwdField === '') {
exit("表 `{$table}` 未找到密码字段,已中止以避免误操作。\n");
}
echo "使用员工表:`{$table}`\n";
echo "字段映射:id={$idField}, 密码={$pwdField}, 工号=" . ($empNoField ?: '(无)') . ", 姓名=" . ($nameField ?: '(无)') . ", 角色=" . ($roleField ?: '(无)') . "\n\n";
// 构造 SELECT(仅选存在的字段)
$selectCols = array_filter([$idField, $empNoField, $nameField, $pwdField, $roleField]);
$sql = "SELECT " . implode(', ', array_map(fn($c) => "`{$c}`", $selectCols)) . " FROM `{$table}`";
$rows = $pdo->query($sql)->fetchAll();
$changed = [];
foreach ($rows as $r) {
$hash = $r[$pwdField] ?? '';
if (!is_string($hash) || $hash === '') continue;
$isWeak = false;
foreach ($weakList as $w) {
if (password_verify($w, $hash)) { $isWeak = true; break; }
}
if (!$isWeak) continue;
$newPwd = generateStrongPassword(16);
$hashNew = password_hash($newPwd, PASSWORD_BCRYPT, ['cost' => 12]);
$pdo->prepare("UPDATE `{$table}` SET `{$pwdField}` = :h WHERE `{$idField}` = :id")
->execute([':h' => $hashNew, ':id' => $r[$idField]]);
$changed[] = [
'emp_no' => $empNoField ? ($r[$empNoField] ?? '') : $r[$idField],
'emp_name' => $nameField ? ($r[$nameField] ?? '') : '',
'new' => $newPwd,
];
}
if ($changed) {
echo "已将以下使用弱密码的账号重置为强密码(请妥善保存):\n";
foreach ($changed as $c) {
echo " emp_no={$c['emp_no']} name={$c['emp_name']} NEW_PASSWORD={$c['new']}\n";
}
echo "\n等效 SQL(如需在 phpMyAdmin 手动执行):\n";
foreach ($changed as $c) {
$h = password_hash($c['new'], PASSWORD_BCRYPT, ['cost' => 12]);
$ident = $empNoField ? "{$empNoField} = '{$c['emp_no']}'" : "{$idField} = '{$c['emp_no']}'";
echo "UPDATE `{$table}` SET `{$pwdField}` = '$h' WHERE {$ident};\n";
}
} else {
echo "未发现使用弱密码的账号,无需处理。\n";
}
function generateStrongPassword($len = 16)
{
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
$lower = 'abcdefghijkmnpqrstuvwxyz';
$digit = '23456789';
$special = '!@#$%^&*';
$all = $upper . $lower . $digit . $special;
$pwd = $upper[random_int(0, strlen($upper) - 1)]
. $lower[random_int(0, strlen($lower) - 1)]
. $digit[random_int(0, strlen($digit) - 1)]
. $special[random_int(0, strlen($special) - 1)];
for ($i = strlen($pwd); $i < $len; $i++) {
$pwd .= $all[random_int(0, strlen($all) - 1)];
}
$arr = str_split($pwd);
shuffle($arr);
return implode('', $arr);
}