弱密码校验

文章讲述了前端开发中如何对用户密码进行校验,包括长度、组合要求、禁止用户名和敏感词的使用。还展示了在登录和密码重置场景下的具体实现,以及相关的Vue组件和路由守卫策略。
摘要由CSDN通过智能技术生成

前端对新增用户或用户重置密码进行校验,规则为:密码需由8-20位数字、字母(区分大小写)、特殊字符组合而成,需包含3种以上组合(大小写、数字、字母、特殊字符);2、密码中不能包含用户名;3、密码中需要进行敏感词过滤(忽略大小写,【敏感词可自定义】 )

  1. 首页点击登录时,若前端校验到当前密码为弱密码(即不满足校验规则),则弹出修改密码框让用户进行密码修改后再重新登录。

在这里插入图片描述
若密码中包含用户名则提示如下:
在这里插入图片描述
若密码不符合校验规则提示如下:
在这里插入图片描述

若密码符合校验规则则可点击确定进行密码修改并更新密码进行重新登录:
在这里插入图片描述

相关代码

弱密码重置组件

src\components\ResetPwd\index.vue

<template>
  <el-dialog title="重置密码" v-model="open" width="650px" append-to-body center style="top: 15%;">
    <el-alert title="密码安全等级过低,请先修改密码后继续登录" :closable="false" type="error" show-icon/>
    <el-form ref="pwdRef" :model="resetForm" label-width="100px" style="margin-top: 20px;" :rules="resetRules">
      <el-form-item label="用户名">
        <el-input v-model="resetForm.username" disabled></el-input>
      </el-form-item>
      <el-form-item label="旧密码" prop="password">
        <el-input style="height: 32px" v-model="resetForm.password" type="password" auto-complete="off"
                  placeholder="密码">
          <template #prefix>
            <svg-icon icon-class="password" class="el-input__icon input-icon"/>
          </template>
        </el-input>
      </el-form-item>
      <el-form-item prop="newPassword">
        <template #label>
          新密码
          <el-tooltip popper-class="tooltip-width" placement="top">
            <template #content>
              <div style="width: 300px">
                <div>
                  1、密码需由8-20位数字、字母(区分大小写)、特殊字符组合而成,需包含3种以上组合(大小写、数字、字母、特殊字符)
                </div>
                <div>
                  2、密码中不能包含用户名
                </div>
                <div>
                  3、密码中需要进行敏感词过滤(忽略大小写,sjb、sdsd、bt )
                </div>
              </div>
            </template>
            <el-icon style="position: relative;top: 8px">
              <InfoFilled/>
            </el-icon>
          </el-tooltip>
        </template>
        <el-input name="newPassword" style="height: 32px" v-model="resetForm.newPassword" type="password"
                  show-password
                  auto-complete="off" placeholder="密码">
          <template #prefix>
            <svg-icon icon-class="password" class="el-input__icon input-icon"/>
          </template>
        </el-input>
      </el-form-item>
      <el-form-item label="确认新密码" prop='confirmPassword'>
        <el-input name="confirmPassword" style="height: 32px" v-model="resetForm.confirmPassword" type="password"
                  show-password
                  auto-complete="off" placeholder="密码">
          <template #prefix>
            <svg-icon icon-class="password" class="el-input__icon input-icon"/>
          </template>
        </el-input>
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup>
import {updateUserPwd} from "@/api/system/user";

const {proxy} = getCurrentInstance();

const open = ref(false)
const resetForm = ref({
  username: "admin",
  password: "",
  newPassword: "",
  confirmPassword: ""
})

const resetRules = ref({
  password: [{required: true, message: "旧密码不能为空", trigger: "blur"}],
  newPassword: [
    {required: true, message: "新密码不能为空", trigger: "blur"},
    {required: true, validator: regPassword, trigger: "blur"}
  ],
  confirmPassword: [
    {required: true, message: "确认密码不能为空", trigger: "blur"},
    {required: true, validator: equalToPassword, trigger: "blur"}
  ]
});


function regPassword(rule, value, callback) {
  let regex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,20}$/;
  let str = ''
  if (!regex.test(value)) {
    str = "请输入8-20位密码并包含特殊字符、大小写、数字"
  }
  let arr = ['sjb', 'sdsd', 'bt']
  arr.forEach(item => {
    if (value.toLowerCase().includes(item.toLowerCase())) {
      str = "密码中不能存在大小写的sjb、sdsd、bt"
    }
  })
  if (value.toLowerCase().includes(resetForm.value.username.toLowerCase())) {
    str = '密码中不能包含用户名'
  }
  if (str) {
    callback(new Error(str));
  } else {
    callback()
  }
}

function equalToPassword(rule, value, callback) {
  if (resetForm.value.newPassword !== value) {
    callback(new Error("两次输入的密码不一致"));
  } else {
    callback();
  }
}

function cancel() {
  open.value = false
}

function init(form) {
  open.value = true
  resetForm.value.username = form.username
  resetForm.value.password = form.password
}

function submitForm() {
  proxy.$refs.pwdRef.validate(valid => {
    if (valid) {
      updateUserPwd(resetForm.value.password, resetForm.value.newPassword).then(response => {
        proxy.$modal.msgSuccess("修改密码成功,请重新登录");
        open.value = false
      });
    }
  });
}

defineExpose({init})
</script>

<style lang="scss">
:deep(.el-tooltip__popper) {
  overflow: hidden;
}
</style>


登录页 login.vue

<template>
<ResetPwd ref="resetPwd"></ResetPwd>
</template>

<script setup>
import ResetPwd from '@/components/ResetPwd'

// 登录方法
function handleLogin() {
  proxy.$refs.loginRef.validate(valid => {
    if (valid) {
      loading.value = true;
      // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
      if (loginForm.value.rememberMe) {
        Cookies.set("username", loginForm.value.username, {expires: 30});
        Cookies.set("password", encrypt(loginForm.value.password), {expires: 30});
        Cookies.set("rememberMe", loginForm.value.rememberMe, {expires: 30});
      } else {
        // 否则移除
        Cookies.remove("username");
        Cookies.remove("password");
        Cookies.remove("rememberMe");
      }
      // 调用action的登录方法
      userStore.login(loginForm.value).then(() => {
        const query = route.query;
        const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
          if (cur !== "redirect") {
            acc[cur] = query[cur];
          }
          return acc;
        }, {});
        // 下面为主要相关代码
        let regex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,}$/;
        if (!regex.test(loginForm.value.password)) {
          proxy.$refs.resetPwd.init(loginForm.value)
          localStorage.isLogin = false
          loading.value = false
          return
        }
        localStorage.isLogin = true
        // 上面为主要相关代码
        router.push({path: redirect.value || "/", query: otherQueryParams});
      }).catch(() => {
        loading.value = false;
        // 重新获取验证码
        if (captchaEnabled.value) {
          getCode();
        }
      });
    }
  });
}
</script>
  1. 个人中心修改密码时也需校验
    在这里插入图片描述相关代码
function regPassword(rule, value, callback) {
  let regex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,20}$/;
  let str = ''
  if (!regex.test(value)) {
    str = "请输入8-20位密码并包含特殊字符、大小写、数字"
  }
  let arr = ['sjb', 'sdsd', 'bt']
  arr.forEach(item => {
    if (value.toLowerCase().includes(item.toLowerCase())) {
      str = "密码中不能存在大小写的sjb、sdsd、bt"
    }
  })
  if (value.toLowerCase().includes(userStore.name.toLowerCase())) {
    str = '密码中不能包含用户名'
  }
  if (str) {
    callback(new Error(str));
  } else {
    callback()
  }
}
  1. 用户中心,可对用户密码进行重置(前端随机生成)

相关代码:

<template>
<el-dialog title="重置密码成功" v-model="dialogVisible" width="30%">
       <div style="margin-bottom: 20px;">重置后新密码为:{{newPwd}}</div>
       <template #footer>
         <div class="dialog-footer">
           <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
         </div>
       </template>
     </el-dialog>
</template>

<script setup>
const dialogVisible = ref(false)
const newPwd = ref('')

/** 重置密码按钮操作 */
function handleResetPwd(row) {
  proxy.$modal.confirm('是否确认重置"' + row.userName + '"的密码').then(() => {
    let pwd = generateStrongPassword(12)
    resetUserPwd(row.userId, pwd).then((response) => {
      if (response.code === 200) {
        dialogVisible.value = true
        newPwd.value = pwd
      }
    });
  }).catch(() => {
  });
};
function generateStrongPassword(length = 8) {
  const minLength = 8;
  const maxLength = 20;
  if (length < minLength || length > maxLength) {
    throw new Error('Password length must be between 8 and 20 characters.');
  }
  // 利用crypto.getRandomValues()生成的随机数值,通过charset映射转换成一个随机的字符序列,最终生成一个随机密码
  
  const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*-_+=.';
  const randomPassword = Array.from(crypto.getRandomValues(new Uint32Array(length)))
      .map(num => charset[num % charset.length])
      .join('');

  return randomPassword;
}
</script>
  1. permission.js 路由守卫,获取到token且密码通过校验才可进行下一步
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()  && localStorage.isLogin === 'true') { // 主要添加代码localStorage.isLogin === 'true'
    to.meta.title && useSettingsStore().setTitle(to.meta.title)
    /* has token*/
    if (to.path === '/login') {
        removeToken()
      next({ path: '/' })
      NProgress.done()
    } else if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      if (useUserStore().roles.length === 0) {
        isRelogin.show = true
        // 判断当前用户是否已拉取完user_info信息
        useUserStore().getInfo().then(() => {
          isRelogin.show = false
          usePermissionStore().generateRoutes().then(accessRoutes => {
            // 根据roles权限生成可访问的路由表
            accessRoutes.forEach(route => {
              if (!isHttp(route.path)) {
                router.addRoute(route) // 动态添加可访问路由表
              }
            })
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
          })
        }).catch(err => {
          useUserStore().logOut().then(() => {
            ElMessage.error(err)
            next({ path: '/' })
          })
        })
      } else {
        next()
      }
    }
  } else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      NProgress.done()
    }
  }
})
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值