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

View File

@ -92,16 +92,36 @@ export const checkHandler: RequestHandler = async (req, res) => {
// 修改密码token
export const UpdateTokenHandler: RequestHandler = async (req, res) => {
const { oldToken, newToken, fromDefault } = req.body;
const { oldToken, newToken } = req.body;
const authorization = req.headers.authorization;
if (isEmpty(newToken)) {
return sendError(res, 'newToken is empty');
}
// 如果不是从默认密码更新,则需要验证旧密码
if (!fromDefault && isEmpty(oldToken)) {
return sendError(res, 'oldToken is required when not updating from default password');
// 强制要求旧密码
if (isEmpty(oldToken)) {
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 {
@ -112,29 +132,18 @@ export const UpdateTokenHandler: RequestHandler = async (req, res) => {
AuthHelper.revokeCredential(Credential);
}
if (fromDefault) {
// 从默认密码更新,直接设置新密码
const currentConfig = await WebUiConfig.GetWebUIConfig();
if (!currentConfig.defaultToken) {
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);
// 使用启动时缓存的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);
return sendSuccess(res, 'Token updated successfully');
} catch (e: any) {

View File

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