chore: 完善系统安全配置校验

This commit is contained in:
2024-05-18 14:39:55 +08:00
parent 3a44a818d8
commit ea435c40ca
5 changed files with 111 additions and 64 deletions

View File

@@ -287,8 +287,8 @@ export interface BasicConfig {
export interface SecurityConfig { export interface SecurityConfig {
PASSWORD_ERROR_LOCK_COUNT: OptionResp PASSWORD_ERROR_LOCK_COUNT: OptionResp
PASSWORD_ERROR_LOCK_MINUTES: OptionResp PASSWORD_ERROR_LOCK_MINUTES: OptionResp
PASSWORD_EXPIRATION_WARNING_DAYS: OptionResp
PASSWORD_EXPIRATION_DAYS: OptionResp PASSWORD_EXPIRATION_DAYS: OptionResp
PASSWORD_EXPIRATION_WARNING_DAYS: OptionResp
PASSWORD_REUSE_POLICY: OptionResp PASSWORD_REUSE_POLICY: OptionResp
PASSWORD_MIN_LENGTH: OptionResp PASSWORD_MIN_LENGTH: OptionResp
PASSWORD_ALLOW_CONTAIN_USERNAME: OptionResp PASSWORD_ALLOW_CONTAIN_USERNAME: OptionResp

View File

@@ -11,7 +11,7 @@
collapsible collapsible
breakpoint="xl" breakpoint="xl"
hide-trigger hide-trigger
:width="220" :width="225"
:collapsed="appStore.menuCollapse" :collapsed="appStore.menuCollapse"
@collapse="handleCollapse" @collapse="handleCollapse"
> >

View File

@@ -57,7 +57,7 @@ const toHome = () => {
flex-shrink: 0; flex-shrink: 0;
} }
.system-name { .system-name {
padding-left: 8px; padding-left: 6px;
white-space: nowrap; white-space: nowrap;
transition: color 0.3s; transition: color 0.3s;
&:hover { &:hover {

View File

@@ -172,22 +172,21 @@ const onUpdate = () => {
// 取消 // 取消
const handleCancel = () => { const handleCancel = () => {
reset()
isUpdate.value = false isUpdate.value = false
} }
const dataList = ref<OptionResp[]>([])
const queryForm = reactive({ const queryForm = reactive({
code: ['SITE_TITLE', 'SITE_COPYRIGHT', 'SITE_LOGO', 'SITE_FAVICON'] code: ['SITE_TITLE', 'SITE_COPYRIGHT', 'SITE_LOGO', 'SITE_FAVICON']
}) })
// 查询列表数据 // 查询列表数据
const getDataList = async () => { const getDataList = async () => {
const res = await listOption(queryForm) const { data } = await listOption(queryForm)
dataList.value = res.data siteFavicon.value = data.find((option) => option.code === 'SITE_FAVICON')
siteFavicon.value = dataList.value.find((option) => option.code === 'SITE_FAVICON') siteLogo.value = data.find((option) => option.code === 'SITE_LOGO')
siteLogo.value = dataList.value.find((option) => option.code === 'SITE_LOGO') siteTitle.value = data.find((option) => option.code === 'SITE_TITLE')
siteTitle.value = dataList.value.find((option) => option.code === 'SITE_TITLE') siteCopyright.value = data.find((option) => option.code === 'SITE_COPYRIGHT')
siteCopyright.value = dataList.value.find((option) => option.code === 'SITE_COPYRIGHT') handleCancel()
reset()
} }
const appStore = useAppStore() const appStore = useAppStore()
@@ -204,7 +203,7 @@ const handleSave = async () => {
}) })
) )
appStore.setSiteConfig(form) appStore.setSiteConfig(form)
handleCancel() await getDataList()
Message.success('保存成功') Message.success('保存成功')
} }

View File

@@ -2,84 +2,92 @@
<a-form <a-form
ref="formRef" ref="formRef"
:model="form" :model="form"
:rules="rules"
size="small" size="small"
:auto-label-width="true" :auto-label-width="true"
label-align="left" label-align="left"
:layout="width >= 500 ? 'horizontal' : 'vertical'" :layout="width >= 500 ? 'horizontal' : 'vertical'"
:disabled="!isUpdate" :disabled="!isUpdate"
scroll-to-first-error
style="margin-top: 10px" style="margin-top: 10px"
> >
<a-list size="small" :bordered="false"> <a-list size="small" :bordered="false">
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_ERROR_LOCK_COUNT.name" :label="securityConfig.PASSWORD_ERROR_LOCK_COUNT.name"
field="PASSWORD_ERROR_LOCK_COUNT" field="PASSWORD_ERROR_LOCK_COUNT"
:help="form.PASSWORD_ERROR_LOCK_COUNT.description" :help="securityConfig.PASSWORD_ERROR_LOCK_COUNT.description"
hide-asterisk
> >
<a-input-number v-model="form.PASSWORD_ERROR_LOCK_COUNT.value" class="input-width" :min="0" :max="10"> <a-input-number v-model="form.PASSWORD_ERROR_LOCK_COUNT" class="input-width" :default-value="0" :precision="0" :min="0" :max="10">
<template #append></template> <template #append></template>
</a-input-number> </a-input-number>
</a-form-item> </a-form-item>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_ERROR_LOCK_MINUTES.name" :label="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.name"
field="PASSWORD_ERROR_LOCK_MINUTES" field="PASSWORD_ERROR_LOCK_MINUTES"
:help="form.PASSWORD_ERROR_LOCK_MINUTES.description" :help="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.description"
hide-asterisk
> >
<a-input-number v-model="form.PASSWORD_ERROR_LOCK_MINUTES.value" class="input-width" :min="1" :max="1440"> <a-input-number v-model="form.PASSWORD_ERROR_LOCK_MINUTES" class="input-width" :precision="0" :min="1" :max="1440">
<template #append>分钟</template> <template #append>分钟</template>
</a-input-number> </a-input-number>
</a-form-item> </a-form-item>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_EXPIRATION_WARNING_DAYS.name" :label="securityConfig.PASSWORD_EXPIRATION_DAYS.name"
field="PASSWORD_EXPIRATION_WARNING_DAYS"
:help="form.PASSWORD_EXPIRATION_WARNING_DAYS.description"
>
<a-input-number v-model="form.PASSWORD_EXPIRATION_WARNING_DAYS.value" class="input-width" :min="0">
<template #append></template>
</a-input-number>
</a-form-item>
</a-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_EXPIRATION_DAYS.name"
field="PASSWORD_EXPIRATION_DAYS" field="PASSWORD_EXPIRATION_DAYS"
:help="form.PASSWORD_EXPIRATION_DAYS.description" :help="securityConfig.PASSWORD_EXPIRATION_DAYS.description"
hide-asterisk
> >
<a-input-number v-model="form.PASSWORD_EXPIRATION_DAYS.value" class="input-width" :min="0" :max="999"> <a-input-number v-model="form.PASSWORD_EXPIRATION_DAYS" class="input-width" :precision="0" :min="0" :max="999">
<template #append></template> <template #append></template>
</a-input-number> </a-input-number>
</a-form-item> </a-form-item>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_REUSE_POLICY.name" :label="securityConfig.PASSWORD_EXPIRATION_WARNING_DAYS.name"
field="PASSWORD_REUSE_POLICY" field="PASSWORD_EXPIRATION_WARNING_DAYS"
:help="form.PASSWORD_REUSE_POLICY.description" :help="securityConfig.PASSWORD_EXPIRATION_WARNING_DAYS.description"
hide-asterisk
> >
<a-input-number v-model="form.PASSWORD_REUSE_POLICY.value" class="input-width" :min="3" :max="32"> <a-input-number v-model="form.PASSWORD_EXPIRATION_WARNING_DAYS" class="input-width" :precision="0" :min="0" :max="998">
<template #append></template>
</a-input-number>
</a-form-item>
</a-list-item>
<a-list-item>
<a-form-item
:label="securityConfig.PASSWORD_REUSE_POLICY.name"
field="PASSWORD_REUSE_POLICY"
:help="securityConfig.PASSWORD_REUSE_POLICY.description"
hide-asterisk
>
<a-input-number v-model="form.PASSWORD_REUSE_POLICY" class="input-width" :precision="0" :min="3" :max="32">
<template #append></template> <template #append></template>
</a-input-number> </a-input-number>
</a-form-item> </a-form-item>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_MIN_LENGTH.name" :label="securityConfig.PASSWORD_MIN_LENGTH.name"
field="PASSWORD_MIN_LENGTH" field="PASSWORD_MIN_LENGTH"
:help="form.PASSWORD_MIN_LENGTH.description" :help="securityConfig.PASSWORD_MIN_LENGTH.description"
hide-asterisk
> >
<a-input-number v-model="form.PASSWORD_MIN_LENGTH.value" class="input-width" :min="8" :max="32" /> <a-input-number v-model="form.PASSWORD_MIN_LENGTH" class="input-width" :precision="0" :min="8" :max="32" />
</a-form-item> </a-form-item>
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_ALLOW_CONTAIN_USERNAME.name" :label="securityConfig.PASSWORD_ALLOW_CONTAIN_USERNAME.name"
field="PASSWORD_ALLOW_CONTAIN_USERNAME" field="PASSWORD_ALLOW_CONTAIN_USERNAME"
> >
<a-switch v-model="form.PASSWORD_ALLOW_CONTAIN_USERNAME.value" type="round" :checked-value="1" :unchecked-value="0"> <a-switch v-model="form.PASSWORD_ALLOW_CONTAIN_USERNAME" type="round" :checked-value="1" :unchecked-value="0">
<template #checked></template> <template #checked></template>
<template #unchecked></template> <template #unchecked></template>
</a-switch> </a-switch>
@@ -87,10 +95,10 @@
</a-list-item> </a-list-item>
<a-list-item> <a-list-item>
<a-form-item <a-form-item
:label="form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.name" :label="securityConfig.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.name"
field="PASSWORD_CONTAIN_SPECIAL_CHARACTERS" field="PASSWORD_CONTAIN_SPECIAL_CHARACTERS"
> >
<a-switch v-model="form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.value" type="round" :checked-value="1" :unchecked-value="0"> <a-switch v-model="form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS" type="round" :checked-value="1" :unchecked-value="0">
<template #checked></template> <template #checked></template>
<template #unchecked></template> <template #unchecked></template>
</a-switch> </a-switch>
@@ -138,31 +146,61 @@
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import { type FormInstance, Message, Modal } from '@arco-design/web-vue' import { type FormInstance, Message, Modal } from '@arco-design/web-vue'
import { type OptionResp, type SecurityConfig, listOption, resetOptionValue, updateOption } from '@/apis' import { type OptionResp, type SecurityConfig, listOption, resetOptionValue, updateOption } from '@/apis'
import { useForm } from '@/hooks'
const { width } = useWindowSize() const { width } = useWindowSize()
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const form = ref<SecurityConfig>({ const { form } = useForm({
PASSWORD_ERROR_LOCK_COUNT: 0,
PASSWORD_ERROR_LOCK_MINUTES: 0,
PASSWORD_EXPIRATION_DAYS: 0,
PASSWORD_EXPIRATION_WARNING_DAYS: 0,
PASSWORD_REUSE_POLICY: 0,
PASSWORD_MIN_LENGTH: 0,
PASSWORD_ALLOW_CONTAIN_USERNAME: 0,
PASSWORD_CONTAIN_SPECIAL_CHARACTERS: 0
})
const rules: FormInstance['rules'] = {
PASSWORD_ERROR_LOCK_COUNT: [{ required: true, message: '请输入值' }],
PASSWORD_ERROR_LOCK_MINUTES: [{ required: true, message: '请输入值' }],
PASSWORD_EXPIRATION_DAYS: [{ required: true, message: '请输入值' }],
PASSWORD_EXPIRATION_WARNING_DAYS: [
{ required: true, message: '请输入值' },
{
validator: (value, callback) => {
if (form.PASSWORD_EXPIRATION_DAYS > 0 && value >= form.PASSWORD_EXPIRATION_DAYS) {
callback('密码到期前的提示时间应小于密码有效期')
} else {
callback()
}
}
}
],
PASSWORD_REUSE_POLICY: [{ required: true, message: '请输入值' }],
PASSWORD_MIN_LENGTH: [{ required: true, message: '请输入值' }]
}
const securityConfig = ref<SecurityConfig>({
PASSWORD_ERROR_LOCK_COUNT: {}, PASSWORD_ERROR_LOCK_COUNT: {},
PASSWORD_ERROR_LOCK_MINUTES: {}, PASSWORD_ERROR_LOCK_MINUTES: {},
PASSWORD_EXPIRATION_WARNING_DAYS: {},
PASSWORD_EXPIRATION_DAYS: {}, PASSWORD_EXPIRATION_DAYS: {},
PASSWORD_EXPIRATION_WARNING_DAYS: {},
PASSWORD_REUSE_POLICY: {}, PASSWORD_REUSE_POLICY: {},
PASSWORD_MIN_LENGTH: {}, PASSWORD_MIN_LENGTH: {},
PASSWORD_ALLOW_CONTAIN_USERNAME: {}, PASSWORD_ALLOW_CONTAIN_USERNAME: {},
PASSWORD_CONTAIN_SPECIAL_CHARACTERS: {} PASSWORD_CONTAIN_SPECIAL_CHARACTERS: {}
}) })
// 重置
const queryForm = { const reset = () => {
code: Object.keys(form.value) form.PASSWORD_ERROR_LOCK_COUNT = securityConfig.value.PASSWORD_ERROR_LOCK_COUNT.value || 0
} form.PASSWORD_ERROR_LOCK_MINUTES = securityConfig.value.PASSWORD_ERROR_LOCK_MINUTES.value || 0
// 查询列表数据 form.PASSWORD_EXPIRATION_DAYS = securityConfig.value.PASSWORD_EXPIRATION_DAYS.value || 0
const getDataList = async () => { form.PASSWORD_EXPIRATION_WARNING_DAYS = securityConfig.value.PASSWORD_EXPIRATION_WARNING_DAYS.value || 0
const { data } = await listOption(queryForm) form.PASSWORD_REUSE_POLICY = securityConfig.value.PASSWORD_REUSE_POLICY.value || 0
form.value = data.reduce((obj: SecurityConfig, option: OptionResp) => { form.PASSWORD_MIN_LENGTH = securityConfig.value.PASSWORD_MIN_LENGTH.value || 0
obj[option.code] = { ...option, value: Number.parseInt(option.value) } form.PASSWORD_ALLOW_CONTAIN_USERNAME = securityConfig.value.PASSWORD_ALLOW_CONTAIN_USERNAME.value || 0
return obj form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS = securityConfig.value.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.value || 0
}, {})
} }
const isUpdate = ref(false) const isUpdate = ref(false)
@@ -171,25 +209,35 @@ const onUpdate = () => {
isUpdate.value = true isUpdate.value = true
} }
// 重置
const reset = () => {
getDataList()
}
// 取消 // 取消
const handleCancel = () => { const handleCancel = () => {
reset() reset()
isUpdate.value = false isUpdate.value = false
} }
const queryForm = {
code: Object.keys(securityConfig.value)
}
// 查询列表数据
const getDataList = async () => {
const { data } = await listOption(queryForm)
securityConfig.value = data.reduce((obj: SecurityConfig, option: OptionResp) => {
obj[option.code] = { ...option, value: Number.parseInt(option.value) }
return obj
}, {})
handleCancel()
}
// 保存 // 保存
const handleSave = async () => { const handleSave = async () => {
const isInvalid = await formRef.value?.validate()
if (isInvalid) return false
await updateOption( await updateOption(
Object.entries(form.value).map(([key, value]) => { Object.entries(form).map(([key, value]) => {
return { code: key, value: value.value } return { code: key, value }
}) })
) )
handleCancel() await getDataList()
Message.success('保存成功') Message.success('保存成功')
} }