😎增加强制修改密码策略
This commit is contained in:
parent
9da7412017
commit
a691937f4e
@ -247,6 +247,7 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
var sysIcpUrl = await GetConfigValue<string>(ConfigConst.SysWebIcpUrl);
|
||||
var sysSecondVer = await GetConfigValue<bool>(ConfigConst.SysSecondVer);
|
||||
var sysCaptcha = await GetConfigValue<bool>(ConfigConst.SysCaptcha);
|
||||
var sysForceChangePassword = await GetConfigValue<bool>(ConfigConst.SysForceChangePassword);
|
||||
|
||||
return new
|
||||
{
|
||||
@ -259,7 +260,8 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
SysIcp = sysIcp,
|
||||
SysIcpUrl = sysIcpUrl,
|
||||
SysSecondVer = sysSecondVer,
|
||||
SysCaptcha = sysCaptcha
|
||||
SysCaptcha = sysCaptcha,
|
||||
SysForceChangePassword = sysForceChangePassword
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -307,7 +307,8 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
user.Password = CryptogramUtil.Encrypt(input.PasswordNew);
|
||||
}
|
||||
|
||||
return await _sysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
|
||||
user.LastChangePasswordTime = DateTime.Now;
|
||||
return await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Password, u.LastChangePasswordTime }).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -321,7 +322,8 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
var password = await _sysConfigService.GetConfigValue<string>(ConfigConst.SysPassword);
|
||||
user.Password = CryptogramUtil.Encrypt(password);
|
||||
await _sysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
|
||||
user.LastChangePasswordTime = null;
|
||||
await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Password, u.LastChangePasswordTime }).ExecuteCommandAsync();
|
||||
|
||||
// 清空密码错误次数
|
||||
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"animate.css": "^4.1.1",
|
||||
"async-validator": "^4.2.5",
|
||||
"axios": "^1.7.6",
|
||||
"axios": "^1.7.7",
|
||||
"countup.js": "^2.8.0",
|
||||
"cropperjs": "^1.6.2",
|
||||
"echarts": "^5.5.1",
|
||||
|
||||
@ -38,7 +38,6 @@ const route = useRoute();
|
||||
const stores = useTagsViewRoutes();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const needUpdate = ref(false);
|
||||
|
||||
// 设置锁屏时组件显示隐藏
|
||||
const setLockScreen = computed(() => {
|
||||
@ -47,28 +46,16 @@ const setLockScreen = computed(() => {
|
||||
return themeConfig.value.isLockScreen ? themeConfig.value.lockScreenTime > 1 : themeConfig.value.lockScreenTime >= 0;
|
||||
});
|
||||
|
||||
// // 获取版本号
|
||||
// const getVersion = computed(() => {
|
||||
// let isVersion = false;
|
||||
// if (route.path !== '/login') {
|
||||
// // @ts-ignore
|
||||
// if ((Local.get('version') && Local.get('version') !== __NEXT_VERSION__) || !Local.get('version')) isVersion = true;
|
||||
// }
|
||||
// return isVersion;
|
||||
// });
|
||||
|
||||
// checkUpdate(() => {
|
||||
// needUpdate.value = true;
|
||||
// }, 60000);
|
||||
|
||||
// 获取全局组件大小
|
||||
const getGlobalComponentSize = computed(() => {
|
||||
return other.globalComponentSize();
|
||||
});
|
||||
|
||||
// 获取全局 i18n
|
||||
const getGlobalI18n = computed(() => {
|
||||
return messages.value[locale.value];
|
||||
});
|
||||
|
||||
// 设置初始化,防止刷新时恢复默认
|
||||
onBeforeMount(() => {
|
||||
// 设置批量第三方 icon 图标
|
||||
@ -76,6 +63,7 @@ onBeforeMount(() => {
|
||||
// 设置批量第三方 js
|
||||
setIntroduction.jsCdn();
|
||||
});
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
@ -94,10 +82,12 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 页面销毁时,关闭监听布局配置/i18n监听
|
||||
onUnmounted(() => {
|
||||
mittBus.off('openSettingsDrawer', () => {});
|
||||
});
|
||||
|
||||
// 监听路由的变化,设置网站标题
|
||||
watch(
|
||||
() => route.path,
|
||||
@ -136,6 +126,8 @@ const loadSysInfo = () => {
|
||||
// 登录验证
|
||||
themeConfig.value.secondVer = data.sysSecondVer;
|
||||
themeConfig.value.captcha = data.sysCaptcha;
|
||||
// 开启强制修改密码
|
||||
themeConfig.value.sysForceChangePassword = data.sysForceChangePassword;
|
||||
|
||||
// 更新 favicon
|
||||
updateFavicon(data.sysLogo);
|
||||
|
||||
@ -79,8 +79,10 @@
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<Search ref="searchRef" />
|
||||
<OnlineUser ref="onlineUserRef" />
|
||||
<ChangePassword ref="changePasswordRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -107,6 +109,7 @@ import { SysAuthApi, SysNoticeApi } from '/@/api-services/api';
|
||||
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue'));
|
||||
const Search = defineAsyncComponent(() => import('/@/layout/navBars/topBar/search.vue'));
|
||||
const OnlineUser = defineAsyncComponent(() => import('/@/views/system/onlineUser/index.vue'));
|
||||
const ChangePassword = defineAsyncComponent(() => import('/@/views/system/user/component/changePassword.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const { locale, t } = useI18n();
|
||||
@ -117,6 +120,7 @@ const { userInfos } = storeToRefs(stores);
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const searchRef = ref();
|
||||
const onlineUserRef = ref();
|
||||
const changePasswordRef = ref();
|
||||
const state = reactive({
|
||||
isScreenfull: false,
|
||||
disabledI18n: 'zh-cn',
|
||||
@ -251,6 +255,9 @@ onMounted(async () => {
|
||||
// // 设置已读
|
||||
// notice.readStatus = 1;
|
||||
// });
|
||||
|
||||
// 强制修改密码
|
||||
forceChangePassword();
|
||||
});
|
||||
// // 页面卸载时
|
||||
// onUnmounted(() => {
|
||||
@ -272,6 +279,16 @@ const receiveNotice = (msg: any) => {
|
||||
timeout: 4500, // 通知显示时间,单位为毫秒
|
||||
});
|
||||
};
|
||||
|
||||
// 开启强制修改密码
|
||||
const forceChangePassword = () => {
|
||||
var forceChangePasswordEnabled = themeConfig.value.sysForceChangePassword ?? true;
|
||||
if (!forceChangePasswordEnabled) return;
|
||||
|
||||
if (userInfos.value.lastChangePasswordTime == null || userInfos.value.lastChangePasswordTime == undefined) {
|
||||
changePasswordRef.value?.openDialog();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -84,6 +84,7 @@ export const useUserInfo = defineStore('userInfo', {
|
||||
posName: d.posName,
|
||||
roles: d.roleIds,
|
||||
authApiList: d.apis,
|
||||
lastChangePasswordTime: d.lastChangePasswordTime,
|
||||
time: new Date().getTime(),
|
||||
};
|
||||
// vue-next-admin 提交Id:225bce7 提交消息:admin-23.03.26:发布v2.4.32版本
|
||||
|
||||
1
Web/src/types/pinia.d.ts
vendored
1
Web/src/types/pinia.d.ts
vendored
@ -100,5 +100,6 @@ declare interface ThemeConfigState {
|
||||
icpUrl: string; // Icp地址
|
||||
secondVer: boolean; // 是否开启二级验证
|
||||
captcha: boolean; // 是否开启验证码
|
||||
sysForceChangePassword: boolean; // 是否开启强制修改密码
|
||||
};
|
||||
}
|
||||
|
||||
105
Web/src/views/system/user/component/changePassword.vue
Normal file
105
Web/src/views/system/user/component/changePassword.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="changePassword-container">
|
||||
<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="700px" :show-close="false">
|
||||
<template #header>
|
||||
<div style="color: #fff">
|
||||
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-View /> </el-icon>
|
||||
<span> 修改密码 </span>
|
||||
</div>
|
||||
</template>
|
||||
<div style="color: red; padding: 10px 10px; background: #faecd8; margin-bottom: 30px">
|
||||
<el-icon style="transform: translateY(2px)"><ele-Bell /></el-icon>
|
||||
<span> 系统安全策略:若长时间没用更新密码,请定期更新密码。 </span>
|
||||
</div>
|
||||
<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="当前密码" prop="passwordOld" :rules="[{ required: true, message: '当前密码不能为空', trigger: 'blur' }]">
|
||||
<el-input v-model="state.ruleForm.passwordOld" type="password" autocomplete="off" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="新密码" prop="passwordNew" :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]">
|
||||
<el-input v-model="state.ruleForm.passwordNew" type="password" autocomplete="off" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="确认密码" prop="passwordNew2" :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]">
|
||||
<el-input v-model="state.passwordNew2" type="password" autocomplete="off" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<!-- <el-button @click="cancel">取 消</el-button> -->
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { sm2 } from 'sm-crypto-v2';
|
||||
|
||||
import { clearAccessTokens, getAPI } from '/@/utils/axios-utils';
|
||||
import { SysUserApi } from '/@/api-services/api';
|
||||
import { ChangePwdInput } from '/@/api-services/models';
|
||||
|
||||
const ruleFormRef = ref();
|
||||
const state = reactive({
|
||||
isShowDialog: false,
|
||||
ruleForm: {} as ChangePwdInput,
|
||||
passwordNew2: '',
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
state.isShowDialog = true;
|
||||
ruleFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// // 取消
|
||||
// const cancel = () => {
|
||||
// state.isShowDialog = false;
|
||||
// };
|
||||
|
||||
// 提交
|
||||
const submit = () => {
|
||||
ruleFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
|
||||
// SM2加密密码
|
||||
const cpwd: ChangePwdInput = { passwordOld: '', passwordNew: '' };
|
||||
const publicKey = window.__env__.VITE_SM_PUBLIC_KEY;
|
||||
cpwd.passwordOld = sm2.doEncrypt(state.ruleForm.passwordOld, publicKey, 1);
|
||||
cpwd.passwordNew = sm2.doEncrypt(state.ruleForm.passwordNew, publicKey, 1);
|
||||
await getAPI(SysUserApi).apiSysUserChangePwdPost(cpwd);
|
||||
|
||||
// 退出系统
|
||||
ElMessageBox.confirm('密码已修改,是否重新登录系统?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
state.isShowDialog = false;
|
||||
clearAccessTokens();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 密码验证
|
||||
const validatePassword = (_rule: any, value: any, callback: any) => {
|
||||
if (state.passwordNew2 != state.ruleForm.passwordNew) {
|
||||
callback(new Error('两次密码不一致!'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// 导出对象
|
||||
defineExpose({ openDialog });
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user