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 {
PASSWORD_ERROR_LOCK_COUNT: OptionResp
PASSWORD_ERROR_LOCK_MINUTES: OptionResp
PASSWORD_EXPIRATION_WARNING_DAYS: OptionResp
PASSWORD_EXPIRATION_DAYS: OptionResp
PASSWORD_EXPIRATION_WARNING_DAYS: OptionResp
PASSWORD_REUSE_POLICY: OptionResp
PASSWORD_MIN_LENGTH: OptionResp
PASSWORD_ALLOW_CONTAIN_USERNAME: OptionResp

View File

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

View File

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

View File

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

View File

@@ -2,84 +2,92 @@
<a-form
ref="formRef"
:model="form"
:rules="rules"
size="small"
:auto-label-width="true"
label-align="left"
:layout="width >= 500 ? 'horizontal' : 'vertical'"
:disabled="!isUpdate"
scroll-to-first-error
style="margin-top: 10px"
>
<a-list size="small" :bordered="false">
<a-list-item>
<a-form-item
:label="form.PASSWORD_ERROR_LOCK_COUNT.name"
:label="securityConfig.PASSWORD_ERROR_LOCK_COUNT.name"
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>
</a-input-number>
</a-form-item>
</a-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_ERROR_LOCK_MINUTES.name"
:label="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.name"
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>
</a-input-number>
</a-form-item>
</a-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_EXPIRATION_WARNING_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"
:label="securityConfig.PASSWORD_EXPIRATION_DAYS.name"
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>
</a-input-number>
</a-form-item>
</a-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_REUSE_POLICY.name"
field="PASSWORD_REUSE_POLICY"
:help="form.PASSWORD_REUSE_POLICY.description"
:label="securityConfig.PASSWORD_EXPIRATION_WARNING_DAYS.name"
field="PASSWORD_EXPIRATION_WARNING_DAYS"
: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>
</a-input-number>
</a-form-item>
</a-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_MIN_LENGTH.name"
:label="securityConfig.PASSWORD_MIN_LENGTH.name"
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-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_ALLOW_CONTAIN_USERNAME.name"
:label="securityConfig.PASSWORD_ALLOW_CONTAIN_USERNAME.name"
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 #unchecked></template>
</a-switch>
@@ -87,10 +95,10 @@
</a-list-item>
<a-list-item>
<a-form-item
:label="form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.name"
:label="securityConfig.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.name"
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 #unchecked></template>
</a-switch>
@@ -138,31 +146,61 @@
import { useWindowSize } from '@vueuse/core'
import { type FormInstance, Message, Modal } from '@arco-design/web-vue'
import { type OptionResp, type SecurityConfig, listOption, resetOptionValue, updateOption } from '@/apis'
import { useForm } from '@/hooks'
const { width } = useWindowSize()
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_MINUTES: {},
PASSWORD_EXPIRATION_WARNING_DAYS: {},
PASSWORD_EXPIRATION_DAYS: {},
PASSWORD_EXPIRATION_WARNING_DAYS: {},
PASSWORD_REUSE_POLICY: {},
PASSWORD_MIN_LENGTH: {},
PASSWORD_ALLOW_CONTAIN_USERNAME: {},
PASSWORD_CONTAIN_SPECIAL_CHARACTERS: {}
})
const queryForm = {
code: Object.keys(form.value)
}
// 查询列表数据
const getDataList = async () => {
const { data } = await listOption(queryForm)
form.value = data.reduce((obj: SecurityConfig, option: OptionResp) => {
obj[option.code] = { ...option, value: Number.parseInt(option.value) }
return obj
}, {})
// 重置
const reset = () => {
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
form.PASSWORD_EXPIRATION_WARNING_DAYS = securityConfig.value.PASSWORD_EXPIRATION_WARNING_DAYS.value || 0
form.PASSWORD_REUSE_POLICY = securityConfig.value.PASSWORD_REUSE_POLICY.value || 0
form.PASSWORD_MIN_LENGTH = securityConfig.value.PASSWORD_MIN_LENGTH.value || 0
form.PASSWORD_ALLOW_CONTAIN_USERNAME = securityConfig.value.PASSWORD_ALLOW_CONTAIN_USERNAME.value || 0
form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS = securityConfig.value.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.value || 0
}
const isUpdate = ref(false)
@@ -171,25 +209,35 @@ const onUpdate = () => {
isUpdate.value = true
}
// 重置
const reset = () => {
getDataList()
}
// 取消
const handleCancel = () => {
reset()
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 isInvalid = await formRef.value?.validate()
if (isInvalid) return false
await updateOption(
Object.entries(form.value).map(([key, value]) => {
return { code: key, value: value.value }
Object.entries(form).map(([key, value]) => {
return { code: key, value }
})
)
handleCancel()
await getDataList()
Message.success('保存成功')
}