Skip to content

Commit

Permalink
feat: 系统配置新增安全设置功能
Browse files Browse the repository at this point in the history
1、在系统配置中增加安全配置,支持配置密码策略
2、移除个人安全设置
3、在账号管理中增加修改密码功能
4、每次登录后检测密码是否过期并提示修改
  • Loading branch information
jskils authored and Charles7c committed May 9, 2024
1 parent c9198b3 commit 395a564
Show file tree
Hide file tree
Showing 15 changed files with 290 additions and 227 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,6 @@ continew-admin-ui # 前端项目
│ │ │ └─ online # 在线用户
│ │ ├─ setting # 设置
│ │ │ ├─ profile # 账号管理
│ │ │ └─ security # 安全设置
│ │ ├─ tool # 系统工具
│ │ │ └─ generator # 代码生成
│ │ └─ system # 系统管理
Expand Down
1 change: 1 addition & 0 deletions src/apis/auth/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface UserInfo {
phone: string
avatar: string
pwdResetTime: string
passwordExpired: boolean
registrationDate: string
deptName: string
roles: string[]
Expand Down
10 changes: 10 additions & 0 deletions src/apis/system/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,16 @@ export interface BasicConfigResp {
site_copyright: string
}

/** 安全配置类型 */
export interface SecurityConfigResp {
password_contain_name: OptionResp
password_error_count: OptionResp
password_lock_minutes: OptionResp
password_min_length: OptionResp
password_special_char: OptionResp
password_update_interval: OptionResp
}

/** 绑定三方账号信息*/
export interface BindSocialAccountRes {
source: string
Expand Down
5 changes: 2 additions & 3 deletions src/layout/components/HeaderRightBar/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@
<a-doption @click="router.push('/setting/profile')">
<span>账号管理</span>
</a-doption>
<a-doption @click="router.push('/setting/security')">
<span>安全设置</span>
</a-doption>
<a-divider :margin="0" />
<a-doption @click="logout">
<span>退出登录</span>
Expand Down Expand Up @@ -111,10 +108,12 @@ const logout = () => {
.user {
cursor: pointer;
color: var(--color-text-1);
.username {
margin-left: 10px;
white-space: nowrap;
}
.arco-icon-down {
transition: all 0.3s;
margin-left: 2px;
Expand Down
8 changes: 1 addition & 7 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ export const constantRoutes: RouteRecordRaw[] = [
name: 'SettingProfile',
component: () => import('@/views/setting/profile/index.vue'),
meta: { title: '账号管理', showInTabs: false }
},
{
path: '/setting/security',
name: 'SettingSecurity',
component: () => import('@/views/setting/security/index.vue'),
meta: { title: '安全设置', showInTabs: false }
}
]
}
Expand All @@ -88,7 +82,7 @@ export function resetRouter() {
router.getRoutes().forEach((route) => {
const { name } = route
// console.log('name', name, path)
if (name && !['Home', 'Setting', 'SettingProfile', 'SettingSecurity'].includes(name.toString())) {
if (name && !['Home', 'Setting', 'SettingProfile'].includes(name.toString())) {
router.hasRoute(name) && router.removeRoute(name)
}
})
Expand Down
1 change: 1 addition & 0 deletions src/stores/modules/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const storeSetup = () => {
phone: '',
avatar: '',
pwdResetTime: '',
passwordExpired: false,
registrationDate: '',
deptName: '',
roles: [],
Expand Down
29 changes: 27 additions & 2 deletions src/views/login/components/account/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@
</a-form-item>
<a-form-item>
<a-space direction="vertical" fill class="w-full">
<a-button class="btn" type="primary" :loading="loading" html-type="submit" size="large" long>立即登录</a-button>
<a-button class="btn" type="primary" :loading="loading" html-type="submit" size="large" long
>立即登录
</a-button>
</a-space>
</a-form-item>
</a-form>
</template>

<script setup lang="ts">
import { getImageCaptcha } from '@/apis'
import { Message, type FormInstance } from '@arco-design/web-vue'
import { Message, type FormInstance, Modal } from '@arco-design/web-vue'
import { useUserStore } from '@/stores'
import { useStorage } from '@vueuse/core'
import { encryptByRsa } from '@/utils/encrypt'
Expand Down Expand Up @@ -92,6 +94,7 @@ const handleLogin = async () => {
const { rememberMe } = loginConfig.value
loginConfig.value.username = rememberMe ? form.username : ''
Message.success('欢迎使用')
checkPasswordExpired()
} catch (error) {
getCaptcha()
form.captcha = ''
Expand All @@ -100,6 +103,26 @@ const handleLogin = async () => {
}
}
const checkPasswordExpired = () => {
if (!userStore.userInfo.passwordExpired) {
return
}
Modal.confirm({
title: '提示',
content: '密码已过期,是否去修改?',
hideCancel: false,
closable: true,
onBeforeOk: async () => {
try {
await router.push({ path: '/setting/profile' })
return true
} catch (error) {
return false
}
}
})
}
const captchaImgBase64 = ref()
// 获取验证码
const getCaptcha = () => {
Expand Down Expand Up @@ -151,6 +174,7 @@ onMounted(() => {
background-color: rgb(var(--danger-1));
border-color: rgb(var(--danger-3));
}
.arco-input-wrapper.arco-input-error:hover {
background-color: rgb(var(--danger-1));
border-color: rgb(var(--danger-6));
Expand All @@ -160,6 +184,7 @@ onMounted(() => {
font-size: 13px;
color: var(--color-text-1);
}
.arco-input-wrapper:hover {
border-color: rgb(var(--arcoblue-6));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,44 @@
<div class="sub-text">
密码至少包含
<span class="sub-text-value">大写字母</span>
<span class="sub-text-value">大写字母</span>
<span class="sub-text-value">小写字母</span>
<span class="sub-text-value">数字</span>
<span class="sub-text-value">特殊字符</span>3种
<span class="sub-text-value" v-if="securityConfig.password_special_char.value == 1">特殊字符</span>
</div>
<div class="sub-text" v-if="securityConfig.password_contain_name.value == 1">
密码不能包含<span class="sub-text-value">正反序用户名</span>
</div>
<div class="sub-text">
密码长度至少
<span class="sub-text-value">
{{ securityConfig.password_min_length.value }}
</span>
</div>
<div class="sub-text">
<div v-if="securityConfig.password_expiration_days.value == 0">未设置密码有效期</div>
<div v-else>
密码有效期
<span class="sub-text-value">
{{ securityConfig.password_expiration_days.value }}
</span>
</div>
</div>
<div class="sub-text">
连续密码错误可重试
<span class="sub-text-value">
{{ securityConfig.password_error_count.value }}
</span>
</div>
<div class="sub-text">
超过错误密码重试次数账号将被锁定
<span class="sub-text-value">
{{ securityConfig.password_lock_minutes.value }}
</span>
分钟
</div>
<div class="sub-text">限制密码长度至少为<span class="sub-text-value">6</span>位</div>
<div class="sub-text">未设置密码有效期</div>
<div class="sub-text">新密码不能与历史前<span class="sub-text-value">N</span>次密码重复</div>
<div class="sub-text">1小时内密码错误可重试<span class="sub-text-value">N</span>次</div>
<div class="sub-text">超过错误密码重试次数账号将被锁定<span class="sub-text-value">N</span>分钟</div>
<a-link class="link">修改规则(未开放)</a-link>
</div>
</div>
</a-card>
Expand All @@ -38,7 +65,7 @@
</template>

<script lang="ts" setup>
import { updateUserPassword } from '@/apis'
import { listOption, type OptionResp, type SecurityConfigResp, updateUserPassword } from '@/apis'
import { Message } from '@arco-design/web-vue'
import { encryptByRsa } from '@/utils/encrypt'
import { type Columns, GiForm } from '@/components/GiForm'
Expand All @@ -60,7 +87,7 @@ const columns: Columns = [
label: '当前密码',
field: 'oldPassword',
type: 'input-password',
rules: [{ required: true, message: '请输入当前密码' }],
rules: [{ required: true, message: '密码长度不正确', maxLength: 32, minLength: 6 }],
hide: () => {
return userInfo.pwdResetTime
}
Expand All @@ -69,13 +96,13 @@ const columns: Columns = [
label: '新密码',
field: 'newPassword',
type: 'input-password',
rules: [{ required: true, message: '请输入新密码' }]
rules: [{ required: true, message: '密码长度不正确', maxLength: 32, minLength: 6 }]
},
{
label: '确认新密码',
field: 'rePassword',
type: 'input-password',
rules: [{ required: true, message: '请再次输入新密码' }],
rules: [{ required: true, message: '密码长度不正确', maxLength: 32, minLength: 6 }],
props: {
placeholder: '请再次输入新密码'
}
Expand Down Expand Up @@ -105,6 +132,14 @@ const onUpdate = async () => {
const save = async () => {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
if (form.newPassword !== form.rePassword) {
Message.error('两次新密码不一致')
return false
}
if (form.newPassword === form.oldPassword) {
Message.error('新密码与旧密码不能相同')
return false
}
try {
await updateUserPassword({
oldPassword: encryptByRsa(form.oldPassword) || '',
Expand All @@ -116,6 +151,28 @@ const save = async () => {
return false
}
}
const securityConfig = ref<SecurityConfigResp>({
password_contain_name: {},
password_error_count: {},
password_expiration_days: {},
password_lock_minutes: {},
password_min_length: {},
password_special_char: {},
password_update_interval: {}
})
// 查询列表数据
const getDataList = async () => {
const { data } = await listOption({ code: Object.keys(securityConfig.value) })
securityConfig.value = data.reduce((obj: SecurityConfigResp, option: OptionResp) => {
obj[option.code] = { ...option, value: parseInt(option.value) }
return obj
}, {})
}
onMounted(() => {
getDataList()
})
</script>

<style lang="scss" scoped></style>
10 changes: 8 additions & 2 deletions src/views/setting/profile/index.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<template>
<div class="gi_page">
<a-row wrap :gutter="16">
<a-row wrap :gutter="16" align="stretch">
<a-col :xs="24" :sm="24" :md="10" :lg="10" :xl="7" :xxl="7">
<LeftBox />
</a-col>
<a-col :xs="24" :sm="24" :md="14" :lg="14" :xl="17" :xxl="17">
<RightBox />
<div>
<PasswordPolicy />
</div>
<div style="margin-top: 16px">
<RightBox />
</div>
</a-col>
</a-row>
</div>
Expand All @@ -14,6 +19,7 @@
<script setup lang="ts">
import LeftBox from './LeftBox.vue'
import RightBox from './RightBox.vue'
import PasswordPolicy from './PasswordPolicy.vue'
defineOptions({ name: 'SettingProfile' })
</script>
Expand Down
49 changes: 0 additions & 49 deletions src/views/setting/security/AccountProtection.vue

This file was deleted.

Loading

0 comments on commit 395a564

Please sign in to comment.