vue 密码强弱设置

  • Post author:
  • Post category:vue




vue 密码强弱设置

在这里插入图片描述

resetPass.vue

<style scoped lang="scss">
  @import "~@a/style/variables.scss";
.password-score {
    display: flex;
    border-radius: 4px;
    p {
      width: 33.3%;
      text-align: center;
      height: 20px;
      line-height: 20px;
      font-size: $font-sm;
      color: $white;
      background-color: #ccc;
      &.weak {
        border-radius: 4px 0 0 4px;
      }
      &.strong {
        border-radius: 0 4px 4px 0;
      }
      &.weak.active {
        background-color: #f05050;
      }
      &.middle.active {
        background-color: #ffbd4a;
      }
      &.strong.active {
        background-color: #81c868;
      }
    }
  }
</style>
<template>
<yv-dialog title="重置密码" :visible.sync="visible">
  <yv-form ref="passwordForm" :model="passwordForm" :rules="passwordFormRule" label-width="100px">
    <yv-form-item label="用户名">
      <yv-tag type="info">root</yv-tag>
    </yv-form-item>
    <yv-form-item label="密码" prop="password">
      <yv-input style="width: 330px;margin-right: 12px" v-model="passwordForm.password" :type="passwordType"
                autocomplete="new-password" :maxlength="30" @change="changePasswordWarning" />
      <yv-checkbox v-model="showPassword" @change="changePassword">显示密码</yv-checkbox>
      <template slot="tips">
        <p class="w400">密码8-30位字符,需同时包含大小写、英文、数字,不能为键盘上连续3位及以上字符,不可包含空格及以下字符# $ % & * &lt; &gt;</p>
        <div v-if="showPasswordWarning" class="password-score mt8" style="width: 310px">
          <p :class="['weak', this.passwordScore <= 60 ? 'active' : '']">弱</p>
          <p :class="['middle', this.passwordScore > 60 && this.passwordScore < 70 ? 'active' : '']">中</p>
          <p :class="['strong', this.passwordScore >= 70 ? 'active' : '']">强</p>
        </div>
      </template>
    </yv-form-item>
    <yv-form-item label="重复密码" prop="password1">
      <yv-input style="width: 330px;margin-right: 12px" v-model="passwordForm.password1" :type="passwordType"
        autocomplete="new-password" :maxlength="30" />
    </yv-form-item>
  </yv-form>
  <div slot="footer" class="dialog-footer">
    <yv-button size="small" @click="cancelDialog">取 消</yv-button>
    <yv-button type="primary" size="small" @click="saveDialog" :loading="saving">
      <span v-if="saving">提交中...</span>
      <span v-else>确定</span>
    </yv-button>
  </div>
</yv-dialog>
</template>

<script>
import ApiInstance from '@/api/module/instance'
import { validatePasswordUtil, calcPwdScore } from '@util'
export default {
  name: "InstanceResetPassword",
  data() {
    const validatePassword = (rule, value, callback) => {
      if (!value) {
        return callback(new Error('密码不能为空'))
      } else {
        if (validatePasswordUtil(value)) {
          callback()
        } else {
          return callback(new Error('密码格式不正确'))
        }
      }
    }
    const validatePassword1 = (rule, value, callback) => {
      if (this.passwordForm.password !== value) {
        return callback(new Error('两次密码输入不一致'))
      }
      callback()
    }
    return {
      visible: false,
      saving: false,
      id:'',
      passwordForm: {
        username: 'root',
        password: '',
        password1: ''
      },
      passwordType: 'password',
      showPassword: false,
      showPasswordWarning: false,
      passwordScore: 0,
      passwordFormRule: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 8, max: 17, message: '长度在 8 到 17 个字符', trigger: 'blur' },
          {validator: validatePassword, trigger: 'blur'}
        ],
        password1: [
          { required: true, message: '请再次输入密码', trigger: 'blur' },
          { min: 8, max: 17, message: '长度在 8 到 17 个字符', trigger: 'blur' },
          {validator: validatePassword1, trigger: 'blur'}
        ],
      }
    }
  },
  methods: {
    changePasswordWarning (val) {
      if (val) {
        this.showPasswordWarning = true
        if (validatePasswordUtil(val)) {
          this.passwordScore = calcPwdScore(val)
        } else {
          this.passwordScore = 59
        }
      } else {
        this.showPasswordWarning = false
      }
    },
    changePassword (val) {
      if (val) {
        this.passwordType = 'text'
      } else {
        this.passwordType = 'password'
      }
    },
    cancelDialog() {
      this.$refs.passwordForm.resetFields()
      this.visible = false
    },
    saveDialog() {
      this.$refs.passwordForm.validate(valid => {
        if (valid) {
          this.saving = true
          ApiInstance.resetPassword(this.id, this.passwordForm).then(() => {
            this.$refs.passwordForm.resetFields()
            this.visible = false
          }).finally(() => {
            this.saving = false;
          })
        }
      })
    },
  }
}
</script>

utils/index.js

// 深拷贝对象
export function deepClone(obj) {
  const _toString = Object.prototype.toString

  // null, undefined, non-object, function
  if (!obj || typeof obj !== 'object') {
    return obj
  }

  // DOM Node
  if (obj.nodeType && 'cloneNode' in obj) {
    return obj.cloneNode(true)
  }

  // Date
  if (_toString.call(obj) === '[object Date]') {
    return new Date(obj.getTime())
  }

  // RegExp
  if (_toString.call(obj) === '[object RegExp]') {
    const flags = []
    if (obj.global) { flags.push('g') }
    if (obj.multiline) { flags.push('m') }
    if (obj.ignoreCase) { flags.push('i') }

    return new RegExp(obj.source, flags.join(''))
  }

  const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}

  for (const key in obj) {
    result[key] = deepClone(obj[key])
  }

  return result
}
/**
 * 校验密码
 * @param {string} password 密码字段
 * @return {Boolean} 是否检验成功
 */
export function validatePasswordUtil(password) {
  let value = password
  if (value === '') return true;
  let isValid = /^(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z])[\x21-\x7e]{8,30}$/g.test(value);
  let repeatVaild = /(\w)*(\w)\2{2}(\w)*/g.test(value);
  if (isValid && !repeatVaild) {
      const k = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '='],
          ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'],
          ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\''],
          ['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/']];
      const K = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+'],
          ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|'],
          ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"'],
          ['Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?']];

      let ks = value.split('');
      let ins = [];
      ks.forEach((o) => {
          LOOP_k:
              for (let i = 0; i < k.length; i++) {
                  for (let j = 0; j < k[i].length; j++) {
                      if (k[i][j] === o) {
                          ins.push(i + ',' + j);
                          break LOOP_k;
                      }
                  }
              }

          LOOP_K:
              for (let m = 0; m < K.length; m++) {
                  for (let n = 0; n < K[m].length; n++) {
                      if (K[m][n] === o) {
                          ins.push(m + ',' + n);
                          break LOOP_K;
                      }
                  }
              }
      });
      let c = 0;
      if (ins.length > 2) {
          let i0 = ins[0].split(','), m = 0;
          for (let i = 1; i < ins.length; i++) {
              let ii = ins[i].split(',');
              if (ii[0] === i0[0] && Math.abs(ii[1] - i0[1]) === 1 && (m === 0 || m === 'x')) {
                  c++;
                  m = 'x';
              } else if (ii[1] === i0[1] && Math.abs(ii[0] - i0[0]) === 1 && (m === 0 || m === 'y')) {
                  c++;
                  m = 'y';
              } else if (Math.abs(ii[0] - i0[0]) === 1 && Math.abs(ii[1] - i0[1]) === 1 && (m === 0 || m == 'z')) {
                  c++;
                  m = 'z';
              } else {
                  c = 0;
                  m = 0;
              }
              i0 = ins[i].split(',');
              if (c > 1) {
                  isValid = false;
                  break;
              }
          }
      }
  }
  return isValid && !repeatVaild
}
/**
 * 计算密码强度
 * 根据规则计算出一个分数,100分制
 * @param {string} password 密码字段
 * @return {number} 密码分数
 */
export function calcPwdScore (password) {
 let score = 0,pwd = password;
 // 密码长度加分 超过8位 +25分 6-8位 5分,小于6位0分
 score += (pwd.length >= 8 ? 25 : (pwd.length >= 6 ? 5 : 0));
 // 全字母 +5分
 if(/^[A-Za-z]$/.test(pwd)) {
  score += 5;
 }
 let arr = pwd.split("");
 let digit = 0, d = 0, x = 0,s = 0, arr2 = [],cs = [0,0,0,0];
 for(let c = 0;  c < arr.length; c++) {
  if(/\d/.test(arr[c])) {
   digit++;
   arr2[c] = 1; // 数字
   cs[0] = 1;
  } else if(/[A-Z]/.test(arr[c])) {
   d++;
   arr2[c] = 2; // 大写
   cs[1] = 1;
  } else if(/[a-z]/.test(arr[c])) {
   x++;
   arr2[c] = 3; // 小写
   cs[2] = 1;
  } else {
   s++;
   arr2[c] = 4; // 特殊字符
   cs[3] = 1;
  }
 }
 // 包含一位数字 +5分 三位以上数字 + 20分
 if(digit === 1) {
  score += 5;
 } else if(digit > 2) {
  score += 15;
 }
 // 大小写字母混合 +15分
 if(d > 0 && x > 0) {
  score += 15;
 } else if(d === arr.length || x === arr.length) {
  // 全大写或全小写 + 5分
  score += 5;
 }
 // 含有特殊字符 +20分
 if(s > 0) {
  score += 20;
 }
 // 计算组合
 var cl = 0;
 for(let k = 0; k < 4; k++) {
  cl += cs[k];
 }

 switch (cl) {
  case 2:
   score += 5;
   break;
  case 3:
   score += 15;
   break;
  case 4:
   score += 25;
   break;
 }

 // 扣分项目 发现连续类型相同 扣1分
 for(let i = 0; i < arr2.length; i++) {
  if(i !== arr2.length - 1 && i < 8) {
      // eslint-disable-next-line no-mixed-spaces-and-tabs
    if(arr2[i] === arr2[i+1]) {
    score--;
   }
  }
 }
 return score;
}



版权声明:本文为qq_43415562原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。