前端对新增用户或用户重置密码进行校验,规则为:密码需由8-20位数字、字母(区分大小写)、特殊字符组合而成,需包含3种以上组合(大小写、数字、字母、特殊字符);2、密码中不能包含用户名;3、密码中需要进行敏感词过滤(忽略大小写,【敏感词可自定义】 )
- 首页点击登录时,若前端校验到当前密码为弱密码(即不满足校验规则),则弹出修改密码框让用户进行密码修改后再重新登录。
若密码中包含用户名则提示如下:
若密码不符合校验规则提示如下:
若密码符合校验规则则可点击确定进行密码修改并更新密码进行重新登录:
相关代码
弱密码重置组件
src\components\ResetPwd\index.vue
<template>
<el-dialog title="重置密码" v-model="open" width="650px" append-to-body center style="top: 15%;">
<el-alert title="密码安全等级过低,请先修改密码后继续登录" :closable="false" type="error" show-icon/>
<el-form ref="pwdRef" :model="resetForm" label-width="100px" style="margin-top: 20px;" :rules="resetRules">
<el-form-item label="用户名">
<el-input v-model="resetForm.username" disabled></el-input>
</el-form-item>
<el-form-item label="旧密码" prop="password">
<el-input style="height: 32px" v-model="resetForm.password" type="password" auto-complete="off"
placeholder="密码">
<template #prefix>
<svg-icon icon-class="password" class="el-input__icon input-icon"/>
</template>
</el-input>
</el-form-item>
<el-form-item prop="newPassword">
<template #label>
新密码
<el-tooltip popper-class="tooltip-width" placement="top">
<template #content>
<div style="width: 300px">
<div>
1、密码需由8-20位数字、字母(区分大小写)、特殊字符组合而成,需包含3种以上组合(大小写、数字、字母、特殊字符)
</div>
<div>
2、密码中不能包含用户名
</div>
<div>
3、密码中需要进行敏感词过滤(忽略大小写,sjb、sdsd、bt )
</div>
</div>
</template>
<el-icon style="position: relative;top: 8px">
<InfoFilled/>
</el-icon>
</el-tooltip>
</template>
<el-input name="newPassword" style="height: 32px" v-model="resetForm.newPassword" type="password"
show-password
auto-complete="off" placeholder="密码">
<template #prefix>
<svg-icon icon-class="password" class="el-input__icon input-icon"/>
</template>
</el-input>
</el-form-item>
<el-form-item label="确认新密码" prop='confirmPassword'>
<el-input name="confirmPassword" style="height: 32px" v-model="resetForm.confirmPassword" type="password"
show-password
auto-complete="off" placeholder="密码">
<template #prefix>
<svg-icon icon-class="password" class="el-input__icon input-icon"/>
</template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {updateUserPwd} from "@/api/system/user";
const {proxy} = getCurrentInstance();
const open = ref(false)
const resetForm = ref({
username: "admin",
password: "",
newPassword: "",
confirmPassword: ""
})
const resetRules = ref({
password: [{required: true, message: "旧密码不能为空", trigger: "blur"}],
newPassword: [
{required: true, message: "新密码不能为空", trigger: "blur"},
{required: true, validator: regPassword, trigger: "blur"}
],
confirmPassword: [
{required: true, message: "确认密码不能为空", trigger: "blur"},
{required: true, validator: equalToPassword, trigger: "blur"}
]
});
function regPassword(rule, value, callback) {
let regex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,20}$/;
let str = ''
if (!regex.test(value)) {
str = "请输入8-20位密码并包含特殊字符、大小写、数字"
}
let arr = ['sjb', 'sdsd', 'bt']
arr.forEach(item => {
if (value.toLowerCase().includes(item.toLowerCase())) {
str = "密码中不能存在大小写的sjb、sdsd、bt"
}
})
if (value.toLowerCase().includes(resetForm.value.username.toLowerCase())) {
str = '密码中不能包含用户名'
}
if (str) {
callback(new Error(str));
} else {
callback()
}
}
function equalToPassword(rule, value, callback) {
if (resetForm.value.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
}
function cancel() {
open.value = false
}
function init(form) {
open.value = true
resetForm.value.username = form.username
resetForm.value.password = form.password
}
function submitForm() {
proxy.$refs.pwdRef.validate(valid => {
if (valid) {
updateUserPwd(resetForm.value.password, resetForm.value.newPassword).then(response => {
proxy.$modal.msgSuccess("修改密码成功,请重新登录");
open.value = false
});
}
});
}
defineExpose({init})
</script>
<style lang="scss">
:deep(.el-tooltip__popper) {
overflow: hidden;
}
</style>
登录页 login.vue
<template>
<ResetPwd ref="resetPwd"></ResetPwd>
</template>
<script setup>
import ResetPwd from '@/components/ResetPwd'
// 登录方法
function handleLogin() {
proxy.$refs.loginRef.validate(valid => {
if (valid) {
loading.value = true;
// 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
if (loginForm.value.rememberMe) {
Cookies.set("username", loginForm.value.username, {expires: 30});
Cookies.set("password", encrypt(loginForm.value.password), {expires: 30});
Cookies.set("rememberMe", loginForm.value.rememberMe, {expires: 30});
} else {
// 否则移除
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove("rememberMe");
}
// 调用action的登录方法
userStore.login(loginForm.value).then(() => {
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
}
return acc;
}, {});
// 下面为主要相关代码
let regex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,}$/;
if (!regex.test(loginForm.value.password)) {
proxy.$refs.resetPwd.init(loginForm.value)
localStorage.isLogin = false
loading.value = false
return
}
localStorage.isLogin = true
// 上面为主要相关代码
router.push({path: redirect.value || "/", query: otherQueryParams});
}).catch(() => {
loading.value = false;
// 重新获取验证码
if (captchaEnabled.value) {
getCode();
}
});
}
});
}
</script>
- 个人中心修改密码时也需校验
相关代码
function regPassword(rule, value, callback) {
let regex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,20}$/;
let str = ''
if (!regex.test(value)) {
str = "请输入8-20位密码并包含特殊字符、大小写、数字"
}
let arr = ['sjb', 'sdsd', 'bt']
arr.forEach(item => {
if (value.toLowerCase().includes(item.toLowerCase())) {
str = "密码中不能存在大小写的sjb、sdsd、bt"
}
})
if (value.toLowerCase().includes(userStore.name.toLowerCase())) {
str = '密码中不能包含用户名'
}
if (str) {
callback(new Error(str));
} else {
callback()
}
}
- 用户中心,可对用户密码进行重置(前端随机生成)
相关代码:
<template>
<el-dialog title="重置密码成功" v-model="dialogVisible" width="30%">
<div style="margin-bottom: 20px;">重置后新密码为:{{newPwd}}</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
const dialogVisible = ref(false)
const newPwd = ref('')
/** 重置密码按钮操作 */
function handleResetPwd(row) {
proxy.$modal.confirm('是否确认重置"' + row.userName + '"的密码').then(() => {
let pwd = generateStrongPassword(12)
resetUserPwd(row.userId, pwd).then((response) => {
if (response.code === 200) {
dialogVisible.value = true
newPwd.value = pwd
}
});
}).catch(() => {
});
};
function generateStrongPassword(length = 8) {
const minLength = 8;
const maxLength = 20;
if (length < minLength || length > maxLength) {
throw new Error('Password length must be between 8 and 20 characters.');
}
// 利用crypto.getRandomValues()生成的随机数值,通过charset映射转换成一个随机的字符序列,最终生成一个随机密码
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*-_+=.';
const randomPassword = Array.from(crypto.getRandomValues(new Uint32Array(length)))
.map(num => charset[num % charset.length])
.join('');
return randomPassword;
}
</script>
- permission.js 路由守卫,获取到token
且密码通过校验
才可进行下一步
router.beforeEach((to, from, next) => {
NProgress.start()
if (getToken() && localStorage.isLogin === 'true') { // 主要添加代码localStorage.isLogin === 'true'
to.meta.title && useSettingsStore().setTitle(to.meta.title)
/* has token*/
if (to.path === '/login') {
removeToken()
next({ path: '/' })
NProgress.done()
} else if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
if (useUserStore().roles.length === 0) {
isRelogin.show = true
// 判断当前用户是否已拉取完user_info信息
useUserStore().getInfo().then(() => {
isRelogin.show = false
usePermissionStore().generateRoutes().then(accessRoutes => {
// 根据roles权限生成可访问的路由表
accessRoutes.forEach(route => {
if (!isHttp(route.path)) {
router.addRoute(route) // 动态添加可访问路由表
}
})
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
})
}).catch(err => {
useUserStore().logOut().then(() => {
ElMessage.error(err)
next({ path: '/' })
})
})
} else {
next()
}
}
} else {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
NProgress.done()
}
}
})