密码强度校验

前言:在前端开发过程中,登录注册页面几乎都是必不可少的页面,有些产品经理就会要求我们对密码进行强度的校验,普通的长度校验很简单,使用正则就可以实现,但是如果需要使用绿、黄、红色来提示用户,又怎么来做呢?废话不多说,直接上代码

校验规则

CheckPassword.js/CheckPassword.ts

/**
 * 数字
 */
const REG_NUMBER: string = '.*\\d+.*'
/**
 * 大写字母
 */
const REG_UPPERCASE: string = '.*[A-Z]+.*'
/**
 * 小写字母
 */
const REG_LOWERCASE: string = '.*[a-z]+.*'
/**
 * 特殊符号(~!@#$%^&*()_+|<>,.?/:;'[]{}\)
 */
const REG_SYMBOL: string = '.*[~!@#$%^&*()_+|<>,.?/:;\'\\[\\]{}"]+.*'
/**
 * 键盘字符表(小写)
 * 非shift键盘字符表
 */
const CHAR_TABLE1: string[][] = [
  ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\0'],
  ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'],
  ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\0', '\0'],
  ['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0'],
]
// /**
//  * shift键盘的字符表
//  */
const CHAR_TABLE2: string[][] = [
  ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\0'],
  ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'],
  ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"', '\0', '\0'],
  ['z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?', '\0', '\0', '\0'],
]

/**
 * 校验密码是否符合条件
 * @param password 密码
 */
export function checkPasswordRule(password: string) {
  if (password === '' || password.length < 8 || password.length > 16) {
    // console.log("长度小于8,或大于16");
    return '密码长度应大于8小于16'
  }
  if (isContinuousChar(password)) {
    // console.log("包含3个及以上相同或字典连续字符");
    return '请勿包含3个及以上相同或连续的字符'
  }
  if (isKeyBoardContinuousChar(password)) {
    // console.log("包含3个及以上键盘连续字符");
    return '请勿包含3个及以上键盘连续字符'
  }
  let i: number = 0
  if (password.match(REG_NUMBER))
    i++
  if (password.match(REG_LOWERCASE))
    i++
  if (password.match(REG_UPPERCASE))
    i++
  if (password.match(REG_SYMBOL))
    i++
  if (i < 3) {
    // console.log(("数字、小写字母、大写字母、特殊字符,至少包含两种"));
    return '数字、小写字母、大写字母、特殊字符,至少包含三种'
  }
  // console.log(i);
  return '校验通过'
}

/**
 * 是否包含3个及以上相同或字典连续字符
 */
function isContinuousChar(password: string) {
  const chars: string[] = password.split('')
  const charCode: number[] = []
  for (let i = 0; i < chars.length - 2; i++)
    charCode[i] = chars[i].charCodeAt(0)

  for (let i = 0; i < charCode.length - 2; i++) {
    const n1 = charCode[i]
    const n2 = charCode[i + 1]
    const n3 = charCode[i + 2]
    // 判断重复字符
    if (n1 === n2 && n1 === n3)
      return true

    // 判断连续字符: 正序 + 倒序
    if ((n1 + 1 === n2 && n1 + 2 === n3) || (n1 - 1 === n2 && n1 - 2 === n3))
      return true
  }
  return false
}
/**
 * 是否包含3个及以上键盘连续字符
 * @param password 待匹配的字符串
 */
function isKeyBoardContinuousChar(password: string) {
  if (password === '')
    return false

  // 考虑大小写,都转换成小写字母
  const lpStrChars: string[] = password.toLowerCase().split('')
  // 获取字符串长度
  const nStrLen: number = lpStrChars.length
  // 定义位置数组:row - 行,col - column 列
  const pRowCharPos: number[] = Array.from({ length: nStrLen }).fill('')
  const pColCharPos: number[] = Array.from({ length: nStrLen }).fill('')
  for (let i = 0; i < nStrLen; i++) {
    const chLower: string = lpStrChars[i]
    pColCharPos[i] = -1
    // 检索在表1中的位置,构建位置数组
    for (let nRowTable1Idx = 0; nRowTable1Idx < 4; nRowTable1Idx++) {
      for (let nColTable1Idx = 0; nColTable1Idx < 13; nColTable1Idx++) {
        if (chLower === CHAR_TABLE1[nRowTable1Idx][nColTable1Idx]) {
          pRowCharPos[i] = nRowTable1Idx
          pColCharPos[i] = nColTable1Idx
        }
      }
    }
    // 在表1中没找到,到表二中去找,找到则continue
    if (pColCharPos[i] >= 0)
      continue

    // 检索在表2中的位置,构建位置数组
    for (let nRowTable2Idx = 0; nRowTable2Idx < 4; nRowTable2Idx++) {
      for (let nColTable2Idx = 0; nColTable2Idx < 13; nColTable2Idx++) {
        if (chLower === CHAR_TABLE2[nRowTable2Idx][nColTable2Idx]) {
          pRowCharPos[i] = nRowTable2Idx
          pColCharPos[i] = nColTable2Idx
        }
      }
    }
  }
  // 匹配坐标连线
  for (let j = 1; j <= nStrLen - 2; j++) {
    // 同一行
    if (pRowCharPos[j - 1] === pRowCharPos[j] && pRowCharPos[j] === pRowCharPos[j + 1]) {
      // 键盘行正向连续(asd)或者键盘行反向连续(dsa)
      if ((pColCharPos[j - 1] + 1 === pColCharPos[j] && pColCharPos[j] + 1 === pColCharPos[j + 1])
                        || (pColCharPos[j + 1] + 1 === pColCharPos[j] && pColCharPos[j] + 1 === pColCharPos[j - 1]))
        return true
    }
    // 同一列
    if (pColCharPos[j - 1] === pColCharPos[j] && pColCharPos[j] === pColCharPos[j + 1]) {
      // 键盘列连续(qaz)或者键盘列反向连续(zaq)
      if ((pRowCharPos[j - 1] + 1 === pRowCharPos[j] && pRowCharPos[j] + 1 === pRowCharPos[j + 1])
                        || (pRowCharPos[j - 1] - 1 === pRowCharPos[j] && pRowCharPos[j] - 1 === pRowCharPos[j + 1]))
        return true
    }
  }
  return false
}

/**
 * 密码强度校验
 */
/**
 * 长度
 * @param str
 */
function length(str: string) {
  if (str.length < 8)
    return 5
  else if (str.length < 12)
    return 10
  else
    return 25
}
/**
 * 字母
 * @param str
 */
function letters(str: string) {
  let count1 = 0
  let count2 = 0
  for (let i = 0; i < str.length; i++) {
    if (str.charAt(i) >= 'a' && str.charAt(i) <= 'z')
      count1++

    if (str.charAt(i) >= 'A' && str.charAt(i) <= 'Z')
      count2++
  }
  if (count1 === 0 && count2 === 0)
    return 0

  if (count1 !== 0 && count2 !== 0)
    return 20

  return 10
}

/**
 * 数字
 * @param str
 */
function numbers(str: string) {
  let count = 0
  for (let i = 0; i < str.length; i++) {
    if (str.charAt(i) >= '0' && str.charAt(i) <= '9')
      count++
  }
  if (count === 0)
    return 0

  if (count === 1)
    return 10

  return 20
}
/**
 * 符号
 * @param str
 */
function symbols(str: string) {
  let count = 0
  for (let i = 0; i < str.length; i++) {
    if ((str.charCodeAt(i) >= 0x21 && str.charCodeAt(i) <= 0x2F)
      || (str.charCodeAt(i) >= 0x3A && str.charCodeAt(i) <= 0x40)
      || (str.charCodeAt(i) >= 0x5B && str.charCodeAt(i) <= 0x60)
      || (str.charCodeAt(i) >= 0x7B && str.charCodeAt(i) <= 0x7E))
      count++
  }
  if (count === 0)
    return 0

  if (count === 1)
    return 10

  return 25
}
/**
 * 得分机制
 * @param str
 */
function rewards(str: string) {
  const letter = letters(str)// 字母
  const number = numbers(str)// 数字
  const symbol = symbols(str)// 符号
  if (letter > 0 && number > 0 && symbol === 0) { // 字母和数字
    return 2
  }
  if (letter === 10 && number > 0 && symbol > 0) { // 字母、数字和符号
    return 3
  }
  if (letter === 20 && number > 0 && symbol > 0) { // 大小写字母、数字和符号
    return 5
  }
  return 0
}
/**
 * 最终评分
 * @param str
 */
export function level(str: string) {
  const lengths = length(str)// 长度
  const letter = letters(str)// 字母
  const number = numbers(str)// 数字
  const symbol = symbols(str)// 符号
  const reward = rewards(str)// 奖励
  const sum = lengths + letter + number + symbol + reward
  console.log(sum)
  if (sum >= 80)
    return '非常强'// 非常安全

  else if (sum >= 60)
    return '强'// 非常强

  else if (sum >= 40)
    return '一般'// 一般

  else if (sum >= 25)
    return '弱'// 弱

  else
    return '非常弱'// 非常弱
}

登录页使用

login.vue

  • script

    <script setup lang="ts">
    // 强度条颜色
    const barColor = ref('')
    // 强度条长度
    const width = ref('')
    const showStrong = ref(false)
    // 强度条文字描述
    const strength = ref('')
    x
    function handlePasswordCheck(val: string) {
    // 可以根据自己需要调整下边的颜色、描述及长度
      const res: string = level(val)
      // 必须通过校验再显示其他颜色
      if (checkPasswordRule(val) === '密码长度应大于8小于16' || checkPasswordRule(val) === '数字、小写字母、大写字母、特殊字符,至少包含三种') {
        barColor.value = 'red'
        strength.value = '非常弱'
        width.value = '30'
        return
      }
      if (res === '非常弱') {
        barColor.value = 'red'
        strength.value = '非常弱'
        width.value = '30'
      }
      else if (res === '一般' && checkPasswordRule(val)) {
        barColor.value = 'yellow'
        strength.value = '一般'
        width.value = '60'
      }
      else if (res === '强') {
        barColor.value = 'green'
        strength.value = '强'
        width.value = '100'
      }
    }
    </scrpit>
    
  • template

    <template>
    	 <el-form-item label="密码" prop="password">
                <el-input
                  v-model.trim="registeredForm.password"
                  type="password"
                  placeholder="请输入密码"
                  @blur="showStrong = false"
                  @focus="showStrong = true"
                />
                <!-- 展示长度条 -->
                <div
                  v-if="registeredForm.password !== '' 
    		&& registeredForm.password !== undefined 
    		&& showStrong"
                  class="bar"
                  :style="{ background: barColor, width: `${width}%` }"
                >
                  <!-- 展示文字 -->
                  <div
                    v-if="registeredForm.password !== '' 
    		&& registeredForm.password !== undefined"
                    class="strength"
                    :style="{ color: barColor }"
                  >
                    {{ strength }}
                  </div>
                </div>
              </el-form-item>
    </template>
    
  • style

<style scoped lang="scss">
.strength {
  font-size: 13px;
  color: #271E25;
  position: relative;
  top: 0;
  left: 0;
  transition: 0.5s all ease;
}

.bar {
  // width: 400px;
  height: 5px;
  background: red;
  transition: 0.5s all ease;
  max-width: 420px;
  margin: 2px 0 5px 5px;
  position: absolute;
  top: 60px;
}
</style>


标题:密码强度校验
作者:mcwu
地址:http://mcongblog.com/articles/2024/06/03/1717382713354.html

    评论
    0 评论
avatar

取消