增加密码校验

This commit is contained in:
BianLzhaoMin 2026-01-04 10:21:30 +08:00
parent a93308d5dd
commit 8521290ee4
3 changed files with 144 additions and 70 deletions

View File

@ -5,30 +5,33 @@
* @returns {Boolean} * @returns {Boolean}
*/ */
export function isPathMatch(pattern, path) { export function isPathMatch(pattern, path) {
const regexPattern = pattern.replace(/\//g, '\\/').replace(/\*\*/g, '.*').replace(/\*/g, '[^\\/]*') const regexPattern = pattern
const regex = new RegExp(`^${regexPattern}$`) .replace(/\//g, '\\/')
return regex.test(path) .replace(/\*\*/g, '.*')
.replace(/\*/g, '[^\\/]*')
const regex = new RegExp(`^${regexPattern}$`)
return regex.test(path)
} }
/** /**
* 判断value字符串是否为空 * 判断value字符串是否为空
* @param {string} value * @param {string} value
* @returns {Boolean} * @returns {Boolean}
*/ */
export function isEmpty(value) { export function isEmpty(value) {
if (value == null || value == "" || value == undefined || value == "undefined") { if (value == null || value == '' || value == undefined || value == 'undefined') {
return true return true
} }
return false return false
} }
/** /**
* 判断url是否是http或https * 判断url是否是http或https
* @param {string} url * @param {string} url
* @returns {Boolean} * @returns {Boolean}
*/ */
export function isHttp(url) { export function isHttp(url) {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
} }
/** /**
@ -37,7 +40,7 @@ export function isHttp(url) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function isExternal(path) { export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path) return /^(https?:|mailto:|tel:)/.test(path)
} }
/** /**
@ -45,8 +48,8 @@ export function isExternal(path) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validUsername(str) { export function validUsername(str) {
const valid_map = ['admin', 'editor'] const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0 return valid_map.indexOf(str.trim()) >= 0
} }
/** /**
@ -54,8 +57,9 @@ export function validUsername(str) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validURL(url) { export function validURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ const reg =
return reg.test(url) /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
} }
/** /**
@ -63,8 +67,8 @@ export function validURL(url) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validLowerCase(str) { export function validLowerCase(str) {
const reg = /^[a-z]+$/ const reg = /^[a-z]+$/
return reg.test(str) return reg.test(str)
} }
/** /**
@ -72,8 +76,8 @@ export function validLowerCase(str) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validUpperCase(str) { export function validUpperCase(str) {
const reg = /^[A-Z]+$/ const reg = /^[A-Z]+$/
return reg.test(str) return reg.test(str)
} }
/** /**
@ -81,8 +85,8 @@ export function validUpperCase(str) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validAlphabets(str) { export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/ const reg = /^[A-Za-z]+$/
return reg.test(str) return reg.test(str)
} }
/** /**
@ -90,8 +94,9 @@ export function validAlphabets(str) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validEmail(email) { export function validEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ const reg =
return reg.test(email) /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
} }
/** /**
@ -99,7 +104,7 @@ export function validEmail(email) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function isString(str) { export function isString(str) {
return typeof str === 'string' || str instanceof String return typeof str === 'string' || str instanceof String
} }
/** /**
@ -107,8 +112,60 @@ export function isString(str) {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function isArray(arg) { export function isArray(arg) {
if (typeof Array.isArray === 'undefined') { if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]' return Object.prototype.toString.call(arg) === '[object Array]'
} }
return Array.isArray(arg) return Array.isArray(arg)
}
/**
* 验证新密码用于 Element UI 表单验证规则
* @param {Object} rule 验证规则对象
* @param {string} value 密码值
* @param {Function} callback 回调函数
*/
export function validateNewPassword(rule, value, callback) {
if (!value) {
callback(new Error('密码不能为空'))
return
}
// 1. 检查密码长度
if (value.length < 8 || value.length > 16) {
callback(new Error('密码长度应为8至16位且必须包含大小写字母及数字及特殊字符'))
return
}
// 2. 检查密码复杂度
const hasUpperCase = /[A-Z]/.test(value)
const hasLowerCase = /[a-z]/.test(value)
const hasDigit = /\d/.test(value)
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value)
if (!hasUpperCase || !hasLowerCase || !hasDigit || !hasSpecialChar) {
callback(new Error('密码长度应为8至16位且必须包含大小写字母及数字及特殊字符'))
return
}
callback() // 验证成功
}
/**
* 同步验证新密码用于 $prompt inputValidator 等场景
* @param {string} value 密码值
* @returns {string|boolean} 返回错误信息字符串或 true验证通过
*/
export function validateNewPasswordSync(value) {
if (!value) {
return '密码不能为空'
}
// 1. 检查密码长度
if (value.length < 8 || value.length > 16) {
return '密码长度应为8至16位且必须包含大小写字母及数字及特殊字符'
}
// 2. 检查密码复杂度
const hasUpperCase = /[A-Z]/.test(value)
const hasLowerCase = /[a-z]/.test(value)
const hasDigit = /\d/.test(value)
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value)
if (!hasUpperCase || !hasLowerCase || !hasDigit || !hasSpecialChar) {
return '密码长度应为8至16位且必须包含大小写字母及数字及特殊字符'
}
return true
} }

View File

@ -531,6 +531,7 @@ import 'splitpanes/dist/splitpanes.css'
import CryptoUtil from '../../../api/crypto.js' import CryptoUtil from '../../../api/crypto.js'
import ComDialog from '@/components/ComDialog/index.vue' import ComDialog from '@/components/ComDialog/index.vue'
import ComButton from '@/components/ComButton/index.vue' import ComButton from '@/components/ComButton/index.vue'
import { validateNewPasswordSync } from '@/utils/validate'
const router = useRouter() const router = useRouter()
const appStore = useAppStore() const appStore = useAppStore()
@ -769,13 +770,7 @@ function handleResetPwd(row) {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
closeOnClickModal: false, closeOnClickModal: false,
inputPattern: /^.{5,20}$/, inputValidator: validateNewPasswordSync,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return '不能包含非法字符:< > " \' \\\ |'
}
},
}) })
.then(({ value }) => { .then(({ value }) => {
resetUserPwd(row.userId, value).then((response) => { resetUserPwd(row.userId, value).then((response) => {

View File

@ -1,59 +1,81 @@
<template> <template>
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px"> <el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword"> <el-form-item label="旧密码" prop="oldPassword">
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password /> <el-input
</el-form-item> v-model="user.oldPassword"
<el-form-item label="新密码" prop="newPassword"> placeholder="请输入旧密码"
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password /> type="password"
</el-form-item> show-password
<el-form-item label="确认密码" prop="confirmPassword"> />
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/> </el-form-item>
</el-form-item> <el-form-item label="新密码" prop="newPassword">
<el-form-item> <el-input
<el-button type="primary" @click="submit">保存</el-button> v-model="user.newPassword"
<el-button type="danger" @click="close">关闭</el-button> placeholder="请输入新密码"
</el-form-item> type="password"
</el-form> 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" @click="submit">保存</el-button>
<el-button type="danger" @click="close">关闭</el-button>
</el-form-item>
</el-form>
</template> </template>
<script setup> <script setup>
import { updateUserPwd } from "@/api/system/user" import { updateUserPwd } from '@/api/system/user'
import { validateNewPassword } from '@/utils/validate'
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const user = reactive({ const user = reactive({
oldPassword: undefined, oldPassword: undefined,
newPassword: undefined, newPassword: undefined,
confirmPassword: undefined confirmPassword: undefined,
}) })
const equalToPassword = (rule, value, callback) => { const equalToPassword = (rule, value, callback) => {
if (user.newPassword !== value) { if (user.newPassword !== value) {
callback(new Error("两次输入的密码不一致")) callback(new Error('两次输入的密码不一致'))
} else { } else {
callback() callback()
} }
} }
const rules = ref({ const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }], oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }], newPassword: [
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }] { required: true, message: '新密码不能为空', trigger: 'blur' },
{ validator: validateNewPassword, trigger: 'blur' },
],
confirmPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
{ validator: equalToPassword, trigger: 'blur' },
],
}) })
/** 提交按钮 */ /** 提交按钮 */
function submit() { function submit() {
proxy.$refs.pwdRef.validate(valid => { proxy.$refs.pwdRef.validate((valid) => {
if (valid) { if (valid) {
updateUserPwd(user.oldPassword, user.newPassword).then(response => { updateUserPwd(user.oldPassword, user.newPassword).then((response) => {
proxy.$modal.msgSuccess("修改成功") proxy.$modal.msgSuccess('修改成功')
}) })
} }
}) })
} }
/** 关闭按钮 */ /** 关闭按钮 */
function close() { function close() {
proxy.$tab.closePage() proxy.$tab.closePage()
} }
</script> </script>