From cdd577a87102ef5be582ffd2fd2a18e40004f922 Mon Sep 17 00:00:00 2001 From: gujiangjiang Date: Wed, 8 Oct 2025 03:49:59 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=A1=AE=E4=BF=9DAdmin=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E7=9A=84=E5=94=AF=E4=B8=80=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要修复内容: 编辑用户页面角色限制: 在编辑用户页面,角色选择的下拉菜单中移除了 Admin 选项,现在只能选择 Manager 或 User。 为了保护唯一的 Admin 账户,当编辑 Admin 用户时,其角色字段将被禁用,防止被误修改。 后端逻辑强化: 在后端服务中增加了额外的安全校验,严格禁止通过API接口将任何用户(包括 Admin 自己)的角色修改为 Admin,也禁止将 Admin 降级为其他角色,确保了 Admin 角色的唯一性和稳定性。 --- backend/src/services/UserService.ts | 82 +++++++++++++++++---------- frontend/src/pages/users/EditUser.tsx | 35 ++++++++++-- 2 files changed, 81 insertions(+), 36 deletions(-) diff --git a/backend/src/services/UserService.ts b/backend/src/services/UserService.ts index 58ec7b0..410a887 100644 --- a/backend/src/services/UserService.ts +++ b/backend/src/services/UserService.ts @@ -1,6 +1,5 @@ import { compare, hash } from "bcryptjs"; import * as repositories from "../repositories"; -import { error } from "console"; export async function getAllUsersService(userRole: string) { try { @@ -74,7 +73,11 @@ export async function createUserService( // 检查角色是否有效,只能创建 manager 或 user const validRoles = ["manager", "user"]; if (!validRoles.includes(userData.role)) { - return { success: false, message: "只能创建 manager 或 user 角色", status: 400 }; + return { + success: false, + message: "只能创建 manager 或 user 角色", + status: 400, + }; } // 哈希密码 @@ -114,7 +117,6 @@ export async function updateUserService( // 检查用户是否存在 const user = await repositories.getFullUserById(id); - if (!user) { return { success: false, message: "用户不存在", status: 404 }; } @@ -128,27 +130,49 @@ export async function updateUserService( return { success: false, message: "无权修改用户角色", status: 403 }; } - // admin 角色不能被修改 - if (user.role === 'admin' && updateData.role && updateData.role !== 'admin') { - return { success: false, message: "不能修改 admin 用户的角色", status: 403 }; + // 关于角色的严格校验 + if (updateData.role) { + // 1. 禁止将任何用户的角色设置为 'admin' + if (updateData.role === "admin") { + return { + success: false, + message: "不允许将用户角色设置为 Admin", + status: 403, + }; + } + // 2. 禁止修改 'admin' 用户自身的角色 + if (user.role === "admin") { + return { + success: false, + message: "Admin 用户的角色不能被修改", + status: 403, + }; + } + // 3. 校验角色是否合法 + const validRoles = ["manager", "user"]; + if (!validRoles.includes(updateData.role)) { + return { success: false, message: "无效的用户角色", status: 400 }; + } } // 准备更新数据 - const updates: any = {}; + const updates: { + username?: string; + email?: string | null; + role?: string; + password?: string; + } = {}; if ( updateData.username !== undefined && updateData.username !== user.username ) { - // 检查新用户名是否已存在 const existingUser = await repositories.checkUserExists( updateData.username ); - if (existingUser) { return { success: false, message: "用户名已存在", status: 400 }; } - updates.username = updateData.username; } @@ -156,25 +180,22 @@ export async function updateUserService( updates.email = updateData.email; } - if (updateData.role !== undefined) { + if (updateData.role !== undefined && updateData.role !== user.role) { updates.role = updateData.role; } - // 如果提供了新密码,则更新密码 if (updateData.password) { updates.password = await hash(updateData.password, 10); } - // 执行更新 - try { - const updatedUser = await repositories.updateUser(id, updates); - return { success: true, user: updatedUser, status: 200 }; - } catch (err) { - if (err instanceof Error && err.message === "没有提供要更新的字段") { - return { success: false, message: "没有提供要更新的字段", status: 400 }; - } - throw err; + // 如果没有要更新的字段,则提前返回 + if (Object.keys(updates).length === 0) { + return { success: true, user: user, message: "没有需要更新的字段", status: 200 }; } + + // 执行更新 + const updatedUser = await repositories.updateUser(id, updates); + return { success: true, user: updatedUser, status: 200 }; } catch (error) { console.error("更新用户错误:", error); return { success: false, message: "更新用户失败", status: 500 }; @@ -199,16 +220,18 @@ export async function deleteUserService( // 检查用户是否存在 const user = await repositories.getUserById(id); - if (!user) { return { success: false, message: "用户不存在", status: 404 }; } - - // admin 用户不能被删除 - if (user.role === 'admin') { - return { success: false, message: "admin 用户不允许被删除", status: 403 }; - } + // admin 用户不能被删除 + if (user.role === "admin") { + return { + success: false, + message: "admin 用户不允许被删除", + status: 403, + }; + } // 执行删除 await repositories.deleteUser(id); @@ -234,19 +257,18 @@ export async function changePasswordService( // 获取用户 const user = await repositories.getFullUserById(id); - if (!user) { return { success: false, message: "用户不存在", status: 404 }; } - // 非管理员需要验证当前密码 + // 如果提供了 currentPassword,则必须验证 if (passwordData.currentPassword) { const isPasswordValid = await compare( passwordData.currentPassword, user.password ); if (!isPasswordValid) { - return { success: false, message: "当前密码无效", status: 400 }; + return { success: false, message: "当前密码无效", status: 400 }; } } diff --git a/frontend/src/pages/users/EditUser.tsx b/frontend/src/pages/users/EditUser.tsx index 9c42656..8de89a1 100644 --- a/frontend/src/pages/users/EditUser.tsx +++ b/frontend/src/pages/users/EditUser.tsx @@ -14,10 +14,12 @@ import { ArrowLeftIcon } from "@radix-ui/react-icons"; import { getUser, updateUser } from "../../api/users"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; +import { User } from "@/types"; const EditUser = () => { const navigate = useNavigate(); const { id } = useParams<{ id: string }>(); + const [user, setUser] = useState(null); const [loading, setLoading] = useState(false); const [formData, setFormData] = useState({ username: "", @@ -32,11 +34,14 @@ const EditUser = () => { setLoading(true); const response = await getUser(Number(id)); if (response.success && response.user) { + setUser(response.user); // 保存原始用户信息 setFormData({ username: response.user.username, email: response.user.email || "", role: response.user.role || "user", }); + } else { + toast.error(response.message || "获取用户信息失败"); } setLoading(false); }; @@ -76,6 +81,10 @@ const EditUser = () => { } }; + if (loading) { + return {t("common.loading")}; + } + return ( @@ -83,7 +92,9 @@ const EditUser = () => { - {t("common.edit")} {t("users.title")} + + {t("common.edit")} {t("users.title")} + @@ -120,14 +131,24 @@ const EditUser = () => { {t("user.role")} * - - Admin - Manager - User + {/* 如果是 admin,只显示 admin 选项,否则显示 manager 和 user */} + {user?.role === "admin" ? ( + Admin + ) : ( + <> + Manager + User + + )} @@ -137,7 +158,9 @@ const EditUser = () => { {t("common.cancel")}