密码加密+增加密码重置页

This commit is contained in:
binbin_pan 2024-05-24 09:35:06 +08:00
parent 21068b0b60
commit 1e9479a9d6
8 changed files with 348 additions and 10 deletions

View File

@ -105,9 +105,12 @@ export const constantRoutes = [
path: '/qrCode/qrCodePage', path: '/qrCode/qrCodePage',
component: () => import('@/views/qrCode/qrCode'), component: () => import('@/views/qrCode/qrCode'),
hidden: true hidden: true
},
{
} path: '/resetPassword',
component: () => import('@/views/resetPassword'),
hidden: true
},
] ]
// 动态路由,基于用户权限动态去加载 // 动态路由,基于用户权限动态去加载

View File

@ -72,6 +72,7 @@ const user = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => { login(username, password, code, uuid).then(res => {
let data = res.data let data = res.data
localStorage.setItem('isCode', data.code)
setToken(data.access_token) setToken(data.access_token)
commit('SET_TOKEN', data.access_token) commit('SET_TOKEN', data.access_token)
localStorage.setItem('token', data.access_token) localStorage.setItem('token', data.access_token)

View File

@ -81,3 +81,14 @@ export function isArray(arg) {
} }
return Array.isArray(arg) return Array.isArray(arg)
} }
/**
* @param {string} str
* @returns {Boolean}
*/
// 密码规则8-20位必须包含字母、数字、特殊字符中的两种
export function validPassword(str) {
const reg = /^((?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[!@#$%^&*()_+\-\=])|(?=.*\d)(?=.*[!@#$%^&*()_+\-\=]))[A-Za-z\d!@#$%^&*()_+\-\=]{8,20}$/
return reg.test(str)
}

View File

@ -302,8 +302,11 @@ export default {
Cookies.remove('rememberMe'); Cookies.remove('rememberMe');
} }
this.$store.dispatch("Login", this.loginForm).then(() => { this.$store.dispatch("Login", this.loginForm).then(() => {
this.$router.push({path:"/"}).catch(() => { if (localStorage.getItem('isCode') == 1) {
}); this.$router.push({ path: '/resetPassword' }).catch(() => {})
} else {
this.$router.push({ path: '/' }).catch(() => {})
}
}).catch(() => { }).catch(() => {
this.loading = false; this.loading = false;
if (this.captchaEnabled) { if (this.captchaEnabled) {

View File

@ -0,0 +1,246 @@
<template>
<div class="login">
<div class="login-bar">
<div class="form-bar">
<h3 class="title">请重置登录密码</h3>
<el-form
ref="form"
:model="user"
:rules="rules"
>
<el-form-item label="旧密码" prop="oldPassword">
<el-input
v-model="user.oldPassword"
placeholder="请输入旧密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="user.newPassword"
placeholder="请输入新密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="user.confirmPassword"
placeholder="请确认新密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 48.5%;" @click="submit"
>确认修改</el-button
>
<el-button style="width: 48.5%;" @click="close">返回登录页</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script>
import { updateUserPwd } from '@/api/system/user'
import { validPassword } from '@/utils/validate'
export default {
data() {
const passwordRegex = (rule, value, callback) => {
if (value.length < 8 || value.length > 20) {
callback(new Error('密码长度在 8 到 20 个字符'))
} else if (!validPassword(value)) {
callback(
new Error('密码须包含数字、字母、特殊符号中的两种以上'),
)
} else if (this.user.oldPassword === value) {
callback(new Error('新密码不能与旧密码相同'))
} else {
callback()
}
}
const equalToPassword = (rule, value, callback) => {
if (this.user.newPassword !== value) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
}
return {
user: {
oldPassword: undefined,
newPassword: undefined,
confirmPassword: undefined,
},
//
rules: {
oldPassword: [
{
required: true,
message: '旧密码不能为空',
trigger: 'blur',
},
],
newPassword: [
{
required: true,
message: '新密码不能为空',
trigger: 'blur',
},
{
required: true,
validator: passwordRegex,
trigger: 'blur',
},
],
confirmPassword: [
{
required: true,
message: '确认密码不能为空',
trigger: 'blur',
},
{
required: true,
validator: equalToPassword,
trigger: 'blur',
},
],
},
}
},
methods: {
submit() {
this.$refs['form'].validate((valid) => {
if (valid) {
updateUserPwd(
this.user.oldPassword,
this.user.newPassword,
).then((response) => {
this.$modal.msgSuccess('修改成功')
//
this.$store.dispatch('LogOut').then(() => {
// location.href = '/login';
// location.href = '/gl/login';
location.href =
process.env.NODE_ENV === 'production-nw'
? '/gl/login'
: '/login'
})
})
}
})
},
close() {
this.$store.dispatch('LogOut').then(() => {
// location.href = '/login';
// location.href = '/gl/login';
location.href =
process.env.NODE_ENV === 'production-nw'
? '/gl/login'
: '/login'
})
},
},
}
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
//display: flex;
//justify-content: center;
//align-items: center;
position: relative;
height: 100%;
background-image: url('../assets/images/login.png');
background-size: 100% 100%;
//background: #1891FF;
}
.title {
margin: 0px auto 30px auto;
//text-align: center;
color: #707070;
}
.login-bar {
position: absolute;
height: auto;
top: 20%;
left: 14%;
width: auto;
height: 500px;
border-radius: 6px;
display: flex;
background: #fff;
text-align: center;
padding-top: 40px;
}
.form-bar {
//height: 500px;
width: 360px;
background: #fff;
}
.login-form {
//border-radius: 6px;
//height: 100%;
background: #ffffff;
width: 400px;
padding: 25px 25px 5px 25px;
.el-input {
height: 45px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 38px;
}
</style>

View File

@ -240,6 +240,15 @@ export default {
getList() { getList() {
this.loading = true; this.loading = true;
listConfig(this.addDateRange(this.queryParams, this.dateRange)).then(response => { listConfig(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
response.rows.forEach(row => {
if (row.configKey == 'sys.user.initPassword') {
this.decryptData(row.configValue, 'CCNXrpassWordKey').then(data => {
row.configValue = data;
}).catch(err => {
console.log('🚀 ~ decryptData ~ err:', err);
});
}
});
this.configList = response.rows; this.configList = response.rows;
this.total = response.total; this.total = response.total;
this.loading = false; this.loading = false;
@ -337,6 +346,26 @@ export default {
refreshCache().then(() => { refreshCache().then(() => {
this.$modal.msgSuccess("刷新成功"); this.$modal.msgSuccess("刷新成功");
}); });
},
async decryptData(encryptedData, keyStr) {
const keyUint8 = new TextEncoder().encode(keyStr);
const encryptedBytes = Uint8Array.from(atob(encryptedData), c => c.charCodeAt(0));
const key = await crypto.subtle.importKey(
'raw',
keyUint8,
{ name: 'AES-CBC', length: 256 }, // 使CBC使
false,
['decrypt']
);
const decryptedData = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv: new Uint8Array(16) }, // 使IV
key,
encryptedBytes
);
return new TextDecoder().decode(decryptedData);
} }
} }
}; };

View File

@ -345,6 +345,7 @@ import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUs
import { getToken } from "@/utils/auth"; import { getToken } from "@/utils/auth";
import Treeselect from "@riophae/vue-treeselect"; import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { validPassword } from '@/utils/validate'
export default { export default {
name: "User", name: "User",
@ -436,7 +437,22 @@ export default {
], ],
password: [ password: [
{ required: true, message: "用户密码不能为空", trigger: "blur" }, { required: true, message: "用户密码不能为空", trigger: "blur" },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' } { min: 8, max: 20, message: '用户密码长度必须介于 8 和 20 之间', trigger: 'blur' },
{
required: true,
validator: (rule, value, callback) => {
if (!validPassword(value)) {
callback(
new Error(
'密码须包含数字、字母、特殊符号中的两种以上',
)
)
} else {
callback()
}
},
trigger: 'blur',
}
], ],
email: [ email: [
{ {
@ -452,7 +468,8 @@ export default {
trigger: "blur" trigger: "blur"
} }
] ]
} },
secretKey: 'CCNXrpassWordKey'
}; };
}, },
watch: { watch: {
@ -465,7 +482,12 @@ export default {
this.getList(); this.getList();
this.getDeptTree(); this.getDeptTree();
this.getConfigKey("sys.user.initPassword").then(response => { this.getConfigKey("sys.user.initPassword").then(response => {
this.initPassword = response.msg; this.decryptData(response.msg, this.secretKey).then((data) => {
console.log('🚀 ~ this.getConfigKey ~ data:', data)
this.initPassword = data
}).catch((error) => {
console.log('🚀 ~ this.getConfigKey ~ error:', error)
})
}); });
}, },
methods: { methods: {
@ -672,6 +694,27 @@ export default {
// //
submitFileForm() { submitFileForm() {
this.$refs.upload.submit(); this.$refs.upload.submit();
},
//
async decryptData(encryptedData, keyStr) {
const keyUint8 = new TextEncoder().encode(keyStr);
const encryptedBytes = Uint8Array.from(atob(encryptedData), c => c.charCodeAt(0));
const key = await crypto.subtle.importKey(
'raw',
keyUint8,
{ name: 'AES-CBC', length: 256 },
false,
['decrypt']
);
const decryptedData = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv: new Uint8Array(16) },
key,
encryptedBytes
);
return new TextDecoder().decode(decryptedData);
} }
} }
}; };

View File

@ -18,15 +18,17 @@
<script> <script>
import { updateUserPwd } from "@/api/system/user"; import { updateUserPwd } from "@/api/system/user";
import { validPassword } from '@/utils/validate'
export default { export default {
data() { data() {
const passwordRegex = (rule, value, callback) => { const passwordRegex = (rule, value, callback) => {
const reg = /^((?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[!@#$%^&*()_+\-\=])|(?=.*\d)(?=.*[!@#$%^&*()_+\-\=]))[A-Za-z\d!@#$%^&*()_+\-\=]{8,20}$/
if (value.length < 8 || value.length > 20) { if (value.length < 8 || value.length > 20) {
callback(new Error("密码长度在 8 到 20 个字符")); callback(new Error("密码长度在 8 到 20 个字符"));
} else if (!reg.test(value)) { } else if (!validPassword(value)) {
callback(new Error("密码须包含数字、字母、特殊符号中的两种以上")); callback(new Error("密码须包含数字、字母、特殊符号中的两种以上"));
} else if (this.user.oldPassword === value) {
callback(new Error('新密码不能与旧密码相同'))
} else { } else {
callback() callback()
} }