feat: 更新密码功能增强,添加新密码强度验证和旧密码检查

This commit is contained in:
时瑾 2025-09-08 22:35:36 +08:00
parent 5499b5fbc9
commit d3013e32e1
No known key found for this signature in database
GPG Key ID: 023F70A1B8F8C196
3 changed files with 84 additions and 30 deletions

View File

@ -14,8 +14,9 @@ const ChangePasswordCard = () => {
const { const {
control, control,
handleSubmit: handleWebuiSubmit, handleSubmit: handleWebuiSubmit,
formState: { isSubmitting }, formState: { isSubmitting, errors },
reset reset,
watch
} = useForm<{ } = useForm<{
oldToken: string oldToken: string
newToken: string newToken: string
@ -29,6 +30,9 @@ const ChangePasswordCard = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [_, setToken] = useLocalStorage(key.token, '') const [_, setToken] = useLocalStorage(key.token, '')
// 监听旧密码的值
const oldTokenValue = watch('oldToken')
const onSubmit = handleWebuiSubmit(async (data) => { const onSubmit = handleWebuiSubmit(async (data) => {
try { try {
// 使用正常密码更新流程 // 使用正常密码更新流程
@ -51,12 +55,24 @@ const ChangePasswordCard = () => {
<Controller <Controller
control={control} control={control}
name="oldToken" name="oldToken"
rules={{
required: '旧密码不能为空',
validate: (value) => {
if (!value || value.trim().length === 0) {
return '旧密码不能为空'
}
return true
}
}}
render={({ field }) => ( render={({ field }) => (
<Input <Input
{...field} {...field}
label="旧密码" label="旧密码"
placeholder="请输入旧密码" placeholder="请输入旧密码"
type="password" type="password"
isRequired
isInvalid={!!errors.oldToken}
errorMessage={errors.oldToken?.message}
/> />
)} )}
/> />
@ -64,12 +80,42 @@ const ChangePasswordCard = () => {
<Controller <Controller
control={control} control={control}
name="newToken" name="newToken"
rules={{
required: '新密码不能为空',
minLength: {
value: 6,
message: '新密码至少需要6个字符'
},
validate: (value) => {
if (!value || value.trim().length === 0) {
return '新密码不能为空'
}
if (value.trim().length !== value.length) {
return '新密码不能包含前后空格'
}
if (value === oldTokenValue) {
return '新密码不能与旧密码相同'
}
// 检查是否包含字母
if (!/[a-zA-Z]/.test(value)) {
return '新密码必须包含字母'
}
// 检查是否包含数字
if (!/[0-9]/.test(value)) {
return '新密码必须包含数字'
}
return true
}
}}
render={({ field }) => ( render={({ field }) => (
<Input <Input
{...field} {...field}
label="新密码" label="新密码"
placeholder="请输入新密码" placeholder="至少6位包含字母和数字"
type="password" type="password"
isRequired
isInvalid={!!errors.newToken}
errorMessage={errors.newToken?.message}
/> />
)} )}
/> />

View File

@ -92,16 +92,36 @@ export const checkHandler: RequestHandler = async (req, res) => {
// 修改密码token // 修改密码token
export const UpdateTokenHandler: RequestHandler = async (req, res) => { export const UpdateTokenHandler: RequestHandler = async (req, res) => {
const { oldToken, newToken, fromDefault } = req.body; const { oldToken, newToken } = req.body;
const authorization = req.headers.authorization; const authorization = req.headers.authorization;
if (isEmpty(newToken)) { if (isEmpty(newToken)) {
return sendError(res, 'newToken is empty'); return sendError(res, 'newToken is empty');
} }
// 如果不是从默认密码更新,则需要验证旧密码 // 强制要求旧密码
if (!fromDefault && isEmpty(oldToken)) { if (isEmpty(oldToken)) {
return sendError(res, 'oldToken is required when not updating from default password'); return sendError(res, 'oldToken is required');
}
// 检查新旧密码是否相同
if (oldToken === newToken) {
return sendError(res, '新密码不能与旧密码相同');
}
// 检查新密码强度
if (newToken.length < 6) {
return sendError(res, '新密码至少需要6个字符');
}
// 检查是否包含字母
if (!/[a-zA-Z]/.test(newToken)) {
return sendError(res, '新密码必须包含字母');
}
// 检查是否包含数字
if (!/[0-9]/.test(newToken)) {
return sendError(res, '新密码必须包含数字');
} }
try { try {
@ -112,29 +132,18 @@ export const UpdateTokenHandler: RequestHandler = async (req, res) => {
AuthHelper.revokeCredential(Credential); AuthHelper.revokeCredential(Credential);
} }
if (fromDefault) { // 使用启动时缓存的token进行验证
// 从默认密码更新,直接设置新密码 const initialToken = getInitialWebUiToken();
const currentConfig = await WebUiConfig.GetWebUIConfig(); if (!initialToken) {
if (!currentConfig.defaultToken) { return sendError(res, 'Server token not initialized');
return sendError(res, 'Current password is not default password');
}
await WebUiConfig.UpdateWebUIConfig({ token: newToken, defaultToken: false });
// 更新内存中的缓存token使新密码立即生效
setInitialWebUiToken(newToken);
} else {
// 正常的密码更新流程 - 使用启动时缓存的token进行验证
const initialToken = getInitialWebUiToken();
if (!initialToken) {
return sendError(res, 'Server token not initialized');
}
if (initialToken !== oldToken) {
return sendError(res, '旧 token 不匹配');
}
// 直接更新配置文件中的token不需要通过WebUiConfig.UpdateToken方法
await WebUiConfig.UpdateWebUIConfig({ token: newToken, defaultToken: false });
// 更新内存中的缓存token使新密码立即生效
setInitialWebUiToken(newToken);
} }
if (initialToken !== oldToken) {
return sendError(res, '旧 token 不匹配');
}
// 直接更新配置文件中的token不需要通过WebUiConfig.UpdateToken方法
await WebUiConfig.UpdateWebUIConfig({ token: newToken, defaultToken: false });
// 更新内存中的缓存token使新密码立即生效
setInitialWebUiToken(newToken);
return sendSuccess(res, 'Token updated successfully'); return sendSuccess(res, 'Token updated successfully');
} catch (e: any) { } catch (e: any) {

View File

@ -1,7 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import { import {
CheckDefaultTokenHandler,
checkHandler, checkHandler,
LoginHandler, LoginHandler,
LogoutHandler, LogoutHandler,