refactor: 将默认密码相关逻辑重构为后端处理 (#1247)

* refactor: 将默认密码相关逻辑重构为后端处理

* refactor: 日志路由进行脱敏,生成随机密码使用node:crypto.randomBytes

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

* feat: 给文件管理添加WebUI配置文件的脱敏处理和验证逻辑

* refactor: 优化网络显示卡片按钮样式和行为,调整按钮属性以提升用户体验

* feat: 增强路径处理逻辑,添加安全验证以防止路径遍历攻击

* feat: 增强文件路径处理逻辑,添加安全验证以防止路径遍历攻击,并优化查询参数提取

* feat: CodeQL不认可 受不了
This commit is contained in:
时瑾
2025-09-11 13:13:00 +08:00
committed by GitHub
parent 5e032fcc6a
commit df2dabfe76
14 changed files with 650 additions and 299 deletions

View File

@@ -1,6 +1,5 @@
import { Input } from '@heroui/input'
import { useLocalStorage } from '@uidotdev/usehooks'
import { useEffect, useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import { useNavigate } from 'react-router-dom'
@@ -12,14 +11,12 @@ import SaveButtons from '@/components/button/save_buttons'
import WebUIManager from '@/controllers/webui_manager'
const ChangePasswordCard = () => {
const [isDefaultToken, setIsDefaultToken] = useState<boolean>(false)
const [isLoadingCheck, setIsLoadingCheck] = useState<boolean>(true)
const {
control,
handleSubmit: handleWebuiSubmit,
formState: { isSubmitting },
reset
formState: { isSubmitting, errors },
reset,
watch
} = useForm<{
oldToken: string
newToken: string
@@ -33,31 +30,13 @@ const ChangePasswordCard = () => {
const navigate = useNavigate()
const [_, setToken] = useLocalStorage(key.token, '')
// 检查是否使用默认密码
useEffect(() => {
const checkDefaultToken = async () => {
try {
const isDefault = await WebUIManager.checkUsingDefaultToken()
setIsDefaultToken(isDefault)
} catch (error) {
console.error('检查默认密码状态失败:', error)
} finally {
setIsLoadingCheck(false)
}
}
checkDefaultToken()
}, [])
// 监听旧密码的值
const oldTokenValue = watch('oldToken')
const onSubmit = handleWebuiSubmit(async (data) => {
try {
if (isDefaultToken) {
// 从默认密码更新
await WebUIManager.changePasswordFromDefault(data.newToken)
} else {
// 正常密码更新
await WebUIManager.changePassword(data.oldToken, data.newToken)
}
// 使用正常密码更新流程
await WebUIManager.changePassword(data.oldToken, data.newToken)
toast.success('修改成功')
setToken('')
@@ -69,53 +48,74 @@ const ChangePasswordCard = () => {
}
})
if (isLoadingCheck) {
return (
<>
<title> - NapCat WebUI</title>
<div className="flex justify-center items-center h-32">
<div className="text-center">...</div>
</div>
</>
)
}
return (
<>
<title> - NapCat WebUI</title>
{isDefaultToken && (
<div className="mb-4 p-3 bg-warning-50 border border-warning-200 rounded-lg">
<p className="text-warning-700 text-sm">
使
</p>
</div>
)}
{!isDefaultToken && (
<Controller
control={control}
name="oldToken"
render={({ field }) => (
<Input
{...field}
label="旧密码"
placeholder="请输入旧密码"
type="password"
/>
)}
/>
)}
<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}
/>
)}
/>
<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={isDefaultToken ? "设置新密码" : "新密码"}
placeholder={isDefaultToken ? "请设置一个安全的新密码" : "请输入新密码"}
label="新密码"
placeholder="至少6位包含字母和数字"
type="password"
isRequired
isInvalid={!!errors.newToken}
errorMessage={errors.newToken?.message}
/>
)}
/>