前言吐槽
本来就很忙,其中一个迭代还没发布上线,就接到一个修改密码的需求,乍一看这个需求没啥难度,细看之后,密码验证有点意思,所以写篇文章记录一下
需求理解
前面基操需求就不阐述了,说一下重点,如下:
- 密码长度不符时,提示:密码长度必须在8到20个字符之间
- 密码纯字母或数字时, 提示:密码必须包含至少一个字母和一个数字
- 相邻5个字母或数字连续时,提示:相邻5个字母或数字不允许相同或连续
第一条是最简单的,稍微有点难度的在后面两条,接下来我将按照顺序来讲解吧
逻辑整理
- 密码长度
- 是否为全数字
- 是否为全字母
- 是否为全空格
- 是否为连续相同的
- 是否为连续数字,例如:12345和54321
- 是否为连续字母(是否区分大小写),例如:abcde和Abcde,以及edcba
代码
先把公共代码放出来,后面逻辑会更清晰一些
//返回结果接口
export interface ResultProps {
success: boolean;
message: string;
}
// 调用入参接口
interface optionsPros {
minLength?: number; // 最小长度, 默认8
maxLength?: number; // 最大长度, 默认20
place?: number; // 匹配连续位数, 默认5位
matchCase?: boolean; // 区分大小写, 默认区分
}
// 转ASCII码
export const toAsciiCode = (str: string) => {
return str.charCodeAt(0);
};
// 错误信息
const createErrorResult = (message = ""): ResultProps => {
return {
success: false,
message,
};
};
一、验证密码长度
// 验证密码长度
export const validateLength = (
pwd: string,
minLength = 8,
maxLength = 20
): ResultProps => {
if (!pwd) {
return createErrorResult("密码不能为空");
}
if (pwd.length < minLength) {
return createErrorResult(`密码不能小于${minLength}位`);
}
if (pwd.length > maxLength) {
return createErrorResult(`密码不能大于${maxLength}位`);
}
return { success: true, message: "" };
};
二、验证全数字,全字母,全空格
//验证至少包含一个数字或者字符,不能全数字,全字符
export const validateString = (pwd: string): ResultProps => {
// 检查是否全为数字
const isAllDigits = /^\d+$/.test(pwd);
// 检查是否全为字母(假设这里包括a-z和A-Z)
const isAllLetters = /^[a-zA-Z]+$/.test(pwd);
// 检查是否至少包含一个非空白字符
const hasAtLeastOneChar = /\S/.test(pwd);
if (isAllDigits) {
return createErrorResult("密码不能全为数字");
}
if (isAllLetters) {
return createErrorResult("密码不能全为字母");
}
if (!hasAtLeastOneChar) {
return createErrorResult("密码不能全为空白字符");
}
return { success: true, message: "" };
};
三、验证连续数字,连续字母
// 验证是否连续 pwd 原始字符串 place匹配的位数 matchCase 区分大小写
export const validateContinuous = (
pwd: string,
place = 5,
matchCase = true
): ResultProps => {
for (let i = 0; i < pwd.length; i++) {
const chartGroup = pwd.slice(i, i + place);
if (chartGroup && chartGroup.length === place) {
const chartGroupArr = chartGroup.split("");
// 检查是否相同
if (validateEqual(chartGroupArr)) {
return createErrorResult(`密码不能连续${place}位相同`);
}
// 检查是否连续数字
if (
validateNum(chartGroupArr) &&
validateNumContinuous(chartGroupArr, place)
) {
return createErrorResult(`密码不能连续${place}位数字`);
}
// 检查是否连续字符
if (validateCharContinuous(chartGroupArr, place, matchCase)) {
return createErrorResult(`密码不能连续${place}位字母`);
}
}
}
return { success: true, message: "" };
};
// 检查是否连续数字
const validateNumContinuous = (arr: string[], place = 5) => {
let count = 0;
// 正序检查
for (let i = 0; i < arr.length; i++) {
if (arr[i + 1] && toAsciiCode(arr[i + 1]) - toAsciiCode(arr[i]) === 1) {
count += 1;
}
}
if (count === place - 1) return true;
count = 0;
// 倒序检查
for (let i = arr.length - 1; i > 0; i--) {
if (toAsciiCode(arr[i - 1]) - toAsciiCode(arr[i]) === 1) {
count += 1;
}
}
return count === place - 1;
};
// 检查是否连续字符
const validateCharContinuous = (arr: string[], place = 5, matchCase = true) => {
let count = 0;
// 正序检查
for (let i = 0; i < arr.length; i++) {
if (
arr[i + 1] &&
toAsciiCode(validateChartCase(arr[i + 1], matchCase)) -
toAsciiCode(validateChartCase(arr[i], matchCase)) ===
1
) {
count += 1;
}
}
if (count === place - 1) return true;
count = 0;
// 倒序检查
for (let i = arr.length - 1; i > 0; i--) {
if (
toAsciiCode(validateChartCase(arr[i - 1], matchCase)) -
toAsciiCode(validateChartCase(arr[i], matchCase)) ===
1
) {
count += 1;
}
}
return count === place - 1;
};
// 检查整个数组是否相同
const validateEqual = (arr: string[]) => {
return arr.every((v) => v === arr[0]);
};
// 检查是否全部都是数字
const validateNum = (arr: string[]) => {
return arr.every((v) => /^\d+$/.test(v));
};
// 检查区分大小写
export const validateChartCase = (val: string, matchCase = true) => {
const chartReg = /^[a-zA-Z]+$/;
return chartReg.test(val) && !matchCase ? val.toLocaleLowerCase() : val;
};
四、完整校验及使用
// 完整密码校验
export const validatePwd = (
pwd: string,
options?: optionsPros
): ResultProps => {
const {
minLength = 8,
maxLength = 20,
place = 5,
matchCase = true,
} = options || {};
// 长度校验
const lengthResult = validateLength(pwd, minLength, maxLength);
if (!lengthResult.success) {
return lengthResult;
}
// 单一类型校验
const strResult = validateString(pwd);
if (!strResult.success) {
return strResult;
}
// 连续校验
const conResult = validateContinuous(pwd, place, matchCase);
if (!conResult.success) {
return conResult;
}
return { success: true, message: "" };
};
不配置调用展示
if (value && value.length) {
const validateResult: ResultProps = validatePwd(value);
if (!validateResult.success) {
return Promise.reject(validateResult.message);
}
return Promise.resolve();
}
配置调用展示
if (value && value.length) {
const validateResult: ResultProps = validatePwd(value, { matchCase: false });
if (!validateResult.success) {
return Promise.reject(validateResult.message);
}
return Promise.resolve();
}
五、测试结果
纯数字测试结果
连续数字测试结果
连续字母测试结果
好了,就不展示所有效果了,感兴趣的小伙伴自己去体验吧
六、完整代码
export interface ResultProps {
success: boolean;
message: string;
}
interface optionsPros {
minLength?: number; // 最小长度, 默认8
maxLength?: number; // 最大长度, 默认20
place?: number; // 匹配连续位数, 默认5位
matchCase?: boolean; // 区分大小写, 默认区分
}
// 验证密码长度
export const validateLength = (
pwd: string,
minLength = 8,
maxLength = 20
): ResultProps => {
if (!pwd) {
return createErrorResult("密码不能为空");
}
if (pwd.length < minLength) {
return createErrorResult(`密码不能小于${minLength}位`);
}
if (pwd.length > maxLength) {
return createErrorResult(`密码不能大于${maxLength}位`);
}
return { success: true, message: "" };
};
//验证至少包含一个数字或者字符,不能全数字,全字符
export const validateString = (pwd: string): ResultProps => {
// 检查是否全为数字
const isAllDigits = /^\d+$/.test(pwd);
// 检查是否全为字母(假设这里包括a-z和A-Z)
const isAllLetters = /^[a-zA-Z]+$/.test(pwd);
// 检查是否至少包含一个非空白字符
const hasAtLeastOneChar = /\S/.test(pwd);
if (isAllDigits) {
return createErrorResult("密码不能全为数字");
}
if (isAllLetters) {
return createErrorResult("密码不能全为字母");
}
if (!hasAtLeastOneChar) {
return createErrorResult("密码不能全为空白字符");
}
return { success: true, message: "" };
};
// 验证是否连续 pwd 原始字符串 place匹配的位数
export const validateContinuous = (
pwd: string,
place = 5,
matchCase = true
): ResultProps => {
for (let i = 0; i < pwd.length; i++) {
const chartGroup = pwd.slice(i, i + place);
if (chartGroup && chartGroup.length === place) {
const chartGroupArr = chartGroup.split("");
// 检查是否相同
if (validateEqual(chartGroupArr)) {
return createErrorResult(`密码不能连续${place}位相同`);
}
// 检查是否连续数字
if (
validateNum(chartGroupArr) &&
validateNumContinuous(chartGroupArr, place)
) {
return createErrorResult(`密码不能连续${place}位数字`);
}
// 检查是否连续字符
if (validateCharContinuous(chartGroupArr, place, matchCase)) {
return createErrorResult(`密码不能连续${place}位字母`);
}
}
}
return { success: true, message: "" };
};
// 完整密码校验
export const validatePwd = (
pwd: string,
options?: optionsPros
): ResultProps => {
const {
minLength = 8,
maxLength = 20,
place = 5,
matchCase = true,
} = options || {};
// 长度校验
const lengthResult = validateLength(pwd, minLength, maxLength);
if (!lengthResult.success) {
return lengthResult;
}
// 单一类型校验
const strResult = validateString(pwd);
if (!strResult.success) {
return strResult;
}
// 连续校验
const conResult = validateContinuous(pwd, place, matchCase);
if (!conResult.success) {
return conResult;
}
return { success: true, message: "" };
};
// 检查整个数组是否相同
const validateEqual = (arr: string[]) => {
return arr.every((v) => v === arr[0]);
};
// 检查是否全部都是数字
const validateNum = (arr: string[]) => {
return arr.every((v) => /^\d+$/.test(v));
};
// 检查是否连续数字
const validateNumContinuous = (arr: string[], place = 5) => {
let count = 0;
// 正序检查
for (let i = 0; i < arr.length; i++) {
if (arr[i + 1] && toAsciiCode(arr[i + 1]) - toAsciiCode(arr[i]) === 1) {
count += 1;
}
}
if (count === place - 1) return true;
count = 0;
// 倒序检查
for (let i = arr.length - 1; i > 0; i--) {
if (toAsciiCode(arr[i - 1]) - toAsciiCode(arr[i]) === 1) {
count += 1;
}
}
return count === place - 1;
};
// 检查是否连续字符
const validateCharContinuous = (arr: string[], place = 5, matchCase = true) => {
let count = 0;
// 正序检查
for (let i = 0; i < arr.length; i++) {
if (
arr[i + 1] &&
toAsciiCode(validateChartCase(arr[i + 1], matchCase)) -
toAsciiCode(validateChartCase(arr[i], matchCase)) ===
1
) {
count += 1;
}
}
if (count === place - 1) return true;
count = 0;
// 倒序检查
for (let i = arr.length - 1; i > 0; i--) {
if (
toAsciiCode(validateChartCase(arr[i - 1], matchCase)) -
toAsciiCode(validateChartCase(arr[i], matchCase)) ===
1
) {
count += 1;
}
}
return count === place - 1;
};
// 检查区分大小写
export const validateChartCase = (val: string, matchCase = true) => {
const chartReg = /^[a-zA-Z]+$/;
return chartReg.test(val) && !matchCase ? val.toLocaleLowerCase() : val;
};
// 转ASCII码
export const toAsciiCode = (str: string) => {
return str.charCodeAt(0);
};
// 错误信息
const createErrorResult = (message = ""): ResultProps => {
return {
success: false,
message,
};
};
总结:一个简单的需求,简单的记录一下,喜欢的小伙伴,点个关注吧➕。坚持原创:前端Robot,如发现文章有误,欢迎评论区指出