import { runLogic } from '@af-mobile-client-vue3/services/api/common' import ModifyPassword from '@af-mobile-client-vue3/views/user/my/comm/ModifyPassword.vue' import { showLoadingToast } from 'vant' import { createApp, h } from 'vue' export const REG_USER_NAME = /^[\u4E00-\u9FA5\w-]{1,20}$/ /** Phone reg */ export const REG_PHONE = /^1((3\d)|(4[014-9])|(5[0-35-9])|(6[2567])|(7[0-8])|(8\d)|(9[0-35-9]))\d{8}$/ /** * Password reg * * any */ export const REG_PWD = /^.*$/ /** * Password reg * * ^:匹配字符串的开始。 * * (?=.{8,}):断言字符串长度至少为8个字符。 * * (?=.*[A-Z]):断言字符串中至少包含一个大写字母。 * * (?=.*[a-z]):断言字符串中至少包含一个小写字母。 * * (?=.*[0-9]):断言字符串中至少包含一个数字。 * * (?=.*\W):断言字符串中至少包含一个非单词字符(例如符号)。 * * .*:匹配任意字符(除换行符外)零次或多次。 * * $:匹配字符串的结束。 */ export const REG_STRONG_PWD = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>~`+=_\\-])[\w!@#$%^&*(),.?":{}|<>~`+=\\-]{8,}$/ export function validatePassword(password: string): string[] { if (REG_STRONG_PWD.test(password)) { return [] } const errors: string[] = [] if (!/.{8,}/.test(password)) { errors.push('密码长度至少为8个字符') } if (!/[A-Z]/.test(password)) { errors.push('密码中至少包含一个大写字母') } if (!/[a-z]/.test(password)) { errors.push('密码中至少包含一个小写字母') } if (!/\d/.test(password)) { errors.push('密码中至少包含一个数字') } if (!/\W/.test(password)) { errors.push('密码中至少包含一个非单词字符(例如符号)') } return errors } // 常见密码列表(可以根据需要扩展) const COMMON_PASSWORDS: string[] = [ 'password', 'password123', '123456', '12345678', 'qwerty', 'abc123', 'Password1', 'password1', '123456789', 'welcome', 'admin', 'letmein', 'monkey', 'dragon', 'master', 'superman', 'qwerty123', 'admin123', 'root', 'pass', 'test', 'guest', 'user', '000000', '111111', '666666', '888888', '999999', 'iloveyou', 'welcome123', 'password!', 'Passw0rd!', 'Passw0rd', 'Password!', 'passw0rd', 'P@ssw0rd', 'P@ssword', 'Password@123', ] // 检查连续数字(3个或以上) function hasConsecutiveNumbers(password: string): boolean { for (let i = 0; i <= password.length - 3; i++) { const substr = password.substring(i, i + 3) if (/^\d{3,}$/.test(substr)) { // 检查是否为连续数字 const digits = substr.split('').map(Number) let isConsecutive = true for (let j = 1; j < digits.length; j++) { if (digits[j] !== digits[j - 1] + 1) { isConsecutive = false break } } if (isConsecutive) return true } } return false } // 检查连续字母(3个或以上) function hasConsecutiveLetters(password: string): boolean { const lowerPassword = password.toLowerCase() for (let i = 0; i <= lowerPassword.length - 3; i++) { const substr = lowerPassword.substring(i, i + 3) if (/^[a-z]{3,}$/.test(substr)) { // 检查是否为连续字母 let isConsecutive = true for (let j = 1; j < substr.length; j++) { if (substr.charCodeAt(j) !== substr.charCodeAt(j - 1) + 1) { isConsecutive = false break } } if (isConsecutive) return true } } return false } // 检查重复字符(3个或以上相同字符) function hasRepeatedChars(password: string): boolean { return /(.)\1{2,}/.test(password) } // 检查键盘连续按键(如 qwerty, asdf 等) function hasKeyboardPattern(password: string): boolean { const keyboardRows: readonly string[] = ['qwertyuiop', 'asdfghjkl', 'zxcvbnm', '1234567890'] const lowerPassword = password.toLowerCase() for (const row of keyboardRows) { for (let i = 0; i <= row.length - 3; i++) { const pattern = row.substring(i, i + 3) if (lowerPassword.includes(pattern)) { return true } } } return false } // 密码校验结果接口 interface PasswordValidationResult { isValid: boolean errors: string[] } // 增强的密码校验函数 export function validateStrongPassword(password: string, commonPasswords: string[]): PasswordValidationResult { const result: PasswordValidationResult = { isValid: true, errors: [], } // 1. 基本格式校验 if (!REG_STRONG_PWD.test(password)) { result.isValid = false result.errors.push('密码必须包含至少8个字符,包括大写字母、小写字母、数字和特殊字符') return result } // 2. 检查是否为常见密码 if (commonPasswords.includes(password) || commonPasswords.includes(password.toLowerCase())) { result.isValid = false result.errors.push('不能使用常见密码') return result } // 3. 检查连续数字 if (hasConsecutiveNumbers(password)) { result.isValid = false result.errors.push('不能包含连续数字') return result } // 4. 检查连续字母 if (hasConsecutiveLetters(password)) { result.isValid = false result.errors.push('不能包含连续字母') return result } // 5. 检查重复字符 if (hasRepeatedChars(password)) { result.isValid = false result.errors.push('不能包含3个或以上相同字符') return result } // 6. 检查键盘模式 if (hasKeyboardPattern(password)) { result.isValid = false result.errors.push('不能包含键盘连续按键模式') return result } return result } /** * 打开修改密码弹窗 * @param ename 用户名 * @param password 当前密码 * @returns Promise 用户是否确认修改密码(true: 修改成功或密码已是强密码,false: 取消修改) */ export function openModifyPasswordModal(ename: string, password: string): Promise { return new Promise((resolve) => { const app = createApp({ render() { return h(ModifyPassword, { 'visible': true, 'ename': ename, 'password': password, 'onUpdate:visible': (visible: boolean) => { if (!visible) { // 弹窗关闭时,如果没有触发 onConfirm,则认为是取消 resolve(false) app.unmount() } }, }) }, }) const div = document.createElement('div') document.body.appendChild(div) app.mount(div) }) } /** * 检查密码是否需要修改 * @param password 当前密码 * @returns 是否需要修改密码 */ export async function checkPasswordNeedModify(password: string): Promise { try { const config: any = await runLogic('getLiuliConfiguration', { configName: 'webMobileConfig' }, 'af-system') const COMMON_PASSWORDS_CONFIG: any = await runLogic('getLiuliConfiguration', { configName: 'COMMON_PASSWORDS' }, 'af-system') const commonPasswords = COMMON_PASSWORDS_CONFIG?.value || COMMON_PASSWORDS // console.warn('commonPasswords', commonPasswords) // 未开启强密码:直接不需要修改 if (!config?.setting?.strongPwd) return false // 默认弱密码 // if (password === '000000') // return true // 强度校验 const passwordValidation = validateStrongPassword(password, commonPasswords) // console.warn('passwordValidation', passwordValidation) if (passwordValidation?.isValid) { return false } let err = '' if (passwordValidation?.errors.length > 0) { err = passwordValidation.errors.join('\n') } else { err = '密码过于简单请修改密码后进行重新登陆' } // console.warn('err', err) showLoadingToast({ zIndex: 2010, message: err, type: 'fail', }) return true } catch (e) { console.error(e) } } /** * 处理登录后的密码校验 * @param ename 用户名 * @param password 当前密码 * @returns Promise 是否通过密码校验(true: 密码是强密码或修改成功,false: 取消修改) */ export async function handleLoginPasswordCheck(ename: string, password: string): Promise { if (await checkPasswordNeedModify(password)) { return await openModifyPasswordModal(ename, password) } // 密码是强密码,直接返回true return true }