😎增加强制修改密码策略
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 sysIcpUrl = await GetConfigValue<string>(ConfigConst.SysWebIcpUrl);
|
||||||
var sysSecondVer = await GetConfigValue<bool>(ConfigConst.SysSecondVer);
|
var sysSecondVer = await GetConfigValue<bool>(ConfigConst.SysSecondVer);
|
||||||
var sysCaptcha = await GetConfigValue<bool>(ConfigConst.SysCaptcha);
|
var sysCaptcha = await GetConfigValue<bool>(ConfigConst.SysCaptcha);
|
||||||
|
var sysForceChangePassword = await GetConfigValue<bool>(ConfigConst.SysForceChangePassword);
|
||||||
|
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
@ -259,7 +260,8 @@ public class SysConfigService : IDynamicApiController, ITransient
|
|||||||
SysIcp = sysIcp,
|
SysIcp = sysIcp,
|
||||||
SysIcpUrl = sysIcpUrl,
|
SysIcpUrl = sysIcpUrl,
|
||||||
SysSecondVer = sysSecondVer,
|
SysSecondVer = sysSecondVer,
|
||||||
SysCaptcha = sysCaptcha
|
SysCaptcha = sysCaptcha,
|
||||||
|
SysForceChangePassword = sysForceChangePassword
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -307,7 +307,8 @@ public class SysUserService : IDynamicApiController, ITransient
|
|||||||
user.Password = CryptogramUtil.Encrypt(input.PasswordNew);
|
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>
|
/// <summary>
|
||||||
@ -321,7 +322,8 @@ public class SysUserService : IDynamicApiController, ITransient
|
|||||||
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||||
var password = await _sysConfigService.GetConfigValue<string>(ConfigConst.SysPassword);
|
var password = await _sysConfigService.GetConfigValue<string>(ConfigConst.SysPassword);
|
||||||
user.Password = CryptogramUtil.Encrypt(password);
|
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}";
|
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"axios": "^1.7.6",
|
"axios": "^1.7.7",
|
||||||
"countup.js": "^2.8.0",
|
"countup.js": "^2.8.0",
|
||||||
"cropperjs": "^1.6.2",
|
"cropperjs": "^1.6.2",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
|
|||||||
@ -38,7 +38,6 @@ const route = useRoute();
|
|||||||
const stores = useTagsViewRoutes();
|
const stores = useTagsViewRoutes();
|
||||||
const storesThemeConfig = useThemeConfig();
|
const storesThemeConfig = useThemeConfig();
|
||||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||||
const needUpdate = ref(false);
|
|
||||||
|
|
||||||
// 设置锁屏时组件显示隐藏
|
// 设置锁屏时组件显示隐藏
|
||||||
const setLockScreen = computed(() => {
|
const setLockScreen = computed(() => {
|
||||||
@ -47,28 +46,16 @@ const setLockScreen = computed(() => {
|
|||||||
return themeConfig.value.isLockScreen ? themeConfig.value.lockScreenTime > 1 : themeConfig.value.lockScreenTime >= 0;
|
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(() => {
|
const getGlobalComponentSize = computed(() => {
|
||||||
return other.globalComponentSize();
|
return other.globalComponentSize();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取全局 i18n
|
// 获取全局 i18n
|
||||||
const getGlobalI18n = computed(() => {
|
const getGlobalI18n = computed(() => {
|
||||||
return messages.value[locale.value];
|
return messages.value[locale.value];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 设置初始化,防止刷新时恢复默认
|
// 设置初始化,防止刷新时恢复默认
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
// 设置批量第三方 icon 图标
|
// 设置批量第三方 icon 图标
|
||||||
@ -76,6 +63,7 @@ onBeforeMount(() => {
|
|||||||
// 设置批量第三方 js
|
// 设置批量第三方 js
|
||||||
setIntroduction.jsCdn();
|
setIntroduction.jsCdn();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 页面加载时
|
// 页面加载时
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@ -94,10 +82,12 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 页面销毁时,关闭监听布局配置/i18n监听
|
// 页面销毁时,关闭监听布局配置/i18n监听
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
mittBus.off('openSettingsDrawer', () => {});
|
mittBus.off('openSettingsDrawer', () => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听路由的变化,设置网站标题
|
// 监听路由的变化,设置网站标题
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
@ -136,6 +126,8 @@ const loadSysInfo = () => {
|
|||||||
// 登录验证
|
// 登录验证
|
||||||
themeConfig.value.secondVer = data.sysSecondVer;
|
themeConfig.value.secondVer = data.sysSecondVer;
|
||||||
themeConfig.value.captcha = data.sysCaptcha;
|
themeConfig.value.captcha = data.sysCaptcha;
|
||||||
|
// 开启强制修改密码
|
||||||
|
themeConfig.value.sysForceChangePassword = data.sysForceChangePassword;
|
||||||
|
|
||||||
// 更新 favicon
|
// 更新 favicon
|
||||||
updateFavicon(data.sysLogo);
|
updateFavicon(data.sysLogo);
|
||||||
|
|||||||
@ -79,8 +79,10 @@
|
|||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
<Search ref="searchRef" />
|
<Search ref="searchRef" />
|
||||||
<OnlineUser ref="onlineUserRef" />
|
<OnlineUser ref="onlineUserRef" />
|
||||||
|
<ChangePassword ref="changePasswordRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -107,6 +109,7 @@ import { SysAuthApi, SysNoticeApi } from '/@/api-services/api';
|
|||||||
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue'));
|
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue'));
|
||||||
const Search = defineAsyncComponent(() => import('/@/layout/navBars/topBar/search.vue'));
|
const Search = defineAsyncComponent(() => import('/@/layout/navBars/topBar/search.vue'));
|
||||||
const OnlineUser = defineAsyncComponent(() => import('/@/views/system/onlineUser/index.vue'));
|
const OnlineUser = defineAsyncComponent(() => import('/@/views/system/onlineUser/index.vue'));
|
||||||
|
const ChangePassword = defineAsyncComponent(() => import('/@/views/system/user/component/changePassword.vue'));
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const { locale, t } = useI18n();
|
const { locale, t } = useI18n();
|
||||||
@ -117,6 +120,7 @@ const { userInfos } = storeToRefs(stores);
|
|||||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||||
const searchRef = ref();
|
const searchRef = ref();
|
||||||
const onlineUserRef = ref();
|
const onlineUserRef = ref();
|
||||||
|
const changePasswordRef = ref();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
isScreenfull: false,
|
isScreenfull: false,
|
||||||
disabledI18n: 'zh-cn',
|
disabledI18n: 'zh-cn',
|
||||||
@ -251,6 +255,9 @@ onMounted(async () => {
|
|||||||
// // 设置已读
|
// // 设置已读
|
||||||
// notice.readStatus = 1;
|
// notice.readStatus = 1;
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
// 强制修改密码
|
||||||
|
forceChangePassword();
|
||||||
});
|
});
|
||||||
// // 页面卸载时
|
// // 页面卸载时
|
||||||
// onUnmounted(() => {
|
// onUnmounted(() => {
|
||||||
@ -272,6 +279,16 @@ const receiveNotice = (msg: any) => {
|
|||||||
timeout: 4500, // 通知显示时间,单位为毫秒
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -84,6 +84,7 @@ export const useUserInfo = defineStore('userInfo', {
|
|||||||
posName: d.posName,
|
posName: d.posName,
|
||||||
roles: d.roleIds,
|
roles: d.roleIds,
|
||||||
authApiList: d.apis,
|
authApiList: d.apis,
|
||||||
|
lastChangePasswordTime: d.lastChangePasswordTime,
|
||||||
time: new Date().getTime(),
|
time: new Date().getTime(),
|
||||||
};
|
};
|
||||||
// vue-next-admin 提交Id:225bce7 提交消息:admin-23.03.26:发布v2.4.32版本
|
// 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地址
|
icpUrl: string; // Icp地址
|
||||||
secondVer: boolean; // 是否开启二级验证
|
secondVer: boolean; // 是否开启二级验证
|
||||||
captcha: 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