react如何實現(xiàn)一個密碼強(qiáng)度檢測器詳解
前言
密碼強(qiáng)度文件校驗器; 注冊帳號的時候我們需要對用戶當(dāng)前的密碼強(qiáng)度進(jìn)行一個評估,這個過程我們需要做一個檢測器,最好寫的靈活點,這樣方便產(chǎn)品修改規(guī)則。
先看下效果吧~~ 下面是截圖對應(yīng)的狀態(tài)

使用
1 參數(shù)傳遞
const PasswordForce = passwordForce({ inputValue, className: 'password-force', });
2 使用
<PasswordForce.View />
3 校驗
檢測是否超出字符PasswordForce.invaildWord
實現(xiàn)例子
我們配置antd實現(xiàn)下密碼輸入框上面綁定一個提示器吧
1,2都是不需要改的,但是實際我們需要監(jiān)聽input的值然后設(shè)置值。于是我們可以定義一個監(jiān)聽修改value的函數(shù)
const [inputValue, setInputValue] = useState('');
const passwordChange = (value: string) => {
setInputValue(value);
};
const onPasswordInput = (e: any) => {
passwordChange(e?.target?.value || '');
};
然后綁定即可,綁定好了我們就可以正常顯示了,但是,如果輸入了非法字符,這時候我們需要通過攔截器攔截。
<Form.Item
...
rules={[
{
required: true,
message: 'Password not empty',
},
({ getFieldValue }) => ({
validator(_, value) {
passwordChange(value);
if (PasswordForce.invaildWord) {
return Promise.reject(
new Error('Password contains invalid characters.'),
);
}
return Promise.resolve();
},
}),
]}
...
好了,使用片結(jié)束,我們實現(xiàn)下吧。
組件編寫
編寫組件
import {
getRuleMatchResult,
IpasswordForce,
IpasswordRule,
isMatchForceResultConfig,
matchResultConfig,
passwordBreakKey,
} from '@/utils/passwordStrengthChecker';
import React, { CSSProperties } from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
import styled from 'styled-components';
interface props {
inputValue: string;
color?: string;
style?: CSSProperties;
className?: string;
customRule?: IpasswordRule[];
}
enum ForceMap {
high = 'High',
middle = 'Mid',
low = 'Low',
}
const boolNumSum = (list: boolean[]) =>
list.reduce<number>(
(previousValue, currentValue) =>
currentValue ? previousValue + 1 : previousValue,
0,
);
const passwordForce: (props: props) => {
View: React.FC;
invaildWord: boolean;
force: IpasswordForce;
} = ({ inputValue, style = {}, className, customRule = [] }) => {
const [force, setforce] = useState<IpasswordForce>(false);
const [invaildWord, setIsInvaildWord] = useState(false);
const inputValueLen = inputValue?.length || 0;
const setData = () => {
setforce(false);
const isFirstWordUp = inputValue[0] === inputValue[0].toLocaleUpperCase();
const ruleRsult = getRuleMatchResult(customRule, inputValue, undefined, '');
const matchNum = boolNumSum(ruleRsult.list.map((e) => e[passwordBreakKey]));
const matchResultConfig: matchResultConfig[] = [
{ min: 0, max: 32, matchNum: 1, value: 'low' },
{ min: 7, max: 32, matchNum: 2, value: 'middle' },
{ min: 7, max: 32, matchNum: 3, value: 'middle' },
{ min: 15, max: 32, matchNum: 3, value: 'high', need: isFirstWordUp },
];
setIsInvaildWord(ruleRsult.invaildWord);
matchResultConfig.forEach((config) => {
isMatchForceResultConfig(config, matchNum, inputValueLen) &&
setforce(config.value);
});
};
useEffect(() => {
inputValue ? setData() : setforce(false);
}, [inputValue]);
return {
View: () =>
force ? (
<PasswordForceWrap {...{ style, className }}>
{ForceMap[force]}
</PasswordForceWrap>
) : (
<></>
),
invaildWord,
force,
};
};
export default passwordForce;
const PasswordForceWrap = styled.span`
color: ${({ color }) => color ?? '#000'};
`;
數(shù)據(jù)結(jié)構(gòu)解析
- list 規(guī)則的集合,每一個規(guī)則都有是否匹配到和規(guī)則名及已規(guī)則數(shù)據(jù)本身。
- map 就是方便直接獲取對應(yīng)規(guī)則的數(shù)據(jù)。
- matchCount 就是匹配到的字符數(shù)
- invaildWord 可以根據(jù)這個來判斷是否有非法字符(超過規(guī)則本身規(guī)定的字符)
流程解析
這個其實就兩個流程
- 根據(jù)輸入的值和規(guī)則獲取處理后的數(shù)據(jù),獲得的數(shù)據(jù)結(jié)構(gòu)如上面所示。
- 然后再編寫具體符合業(yè)務(wù)需求的config數(shù)據(jù)交給isMatchForceResultConfig函數(shù)去匹配設(shè)置強(qiáng)度
嗯。 業(yè)務(wù)代碼差不多就這么多了。 然后里面關(guān)于依賴那就是屬于基本不會改動的代碼,基于下面底層的文件,在業(yè)務(wù)代碼我們可以配置出很復(fù)雜的校驗器,這部分代碼我們可以在其他文件上實現(xiàn)。
底層代碼解析
讓我們來康康吧。
下面是純ts代碼,可以運行任意框架哦。
passwordStrengthChecker.ts
import { numberList, specialList, wordList } from './constants';
type map = <U, T>(opstion: {
array: U[];
range: number;
matchList: T[];
tokenMap: (updateItem: T, token: U, index: number) => T;
breakKey?: string;
arrayMap?: (item: U, index: number) => void;
}) => T[];
/**
* match array and set
*/
export const setArrayMatch: map = ({
array,
range,
matchList,
breakKey,
tokenMap,
arrayMap,
}) => {
const tokenLen = array.length;
for (let tokenIndex = tokenLen - 1; tokenIndex >= 0; tokenIndex--) {
const arrayToken = array[tokenIndex];
arrayMap && arrayMap(arrayToken, tokenIndex);
for (let findIndex = range - 1; findIndex >= 0; findIndex--) {
matchList = matchList.map((item) =>
tokenMap(item, arrayToken, findIndex),
);
}
if (breakKey && !matchList.map((e) => (e as any)[breakKey]).includes(false))
break;
}
return matchList;
};
export const passwordBreakKey = 'isMatch';
export type IpasswordRule = {
list: string[];
isMatch: boolean;
name: string;
};
export const defaultPasswordRuleList = [
{ name: 'special', list: specialList },
{ name: 'num', list: numberList },
{ name: 'word', list: wordList },
];
type PickValue<T, K extends keyof T> = T[K];
export const getRuleMatchResult: (
customRule: IpasswordRule[],
inputValue: string,
disableDefaultRule?: boolean,
breakKey?: string,
) => {
list: IpasswordRule[];
map: Map<PickValue<IpasswordRule, 'name'>, boolean>;
matchCount: number;
invaildWord: boolean;
} = (customRule, inputValue, disableDefaultRule = true, breakKey) => {
let ruleList = [
...(disableDefaultRule ? defaultPasswordRuleList : []),
...customRule,
].map((item) => ({ ...item, [passwordBreakKey]: false }));
const range = Math.max(...ruleList.map((ruleItem) => ruleItem.list.length));
let matchCount = 0;
ruleList = setArrayMatch<string, IpasswordRule>({
array: inputValue.split(''),
range,
matchList: ruleList,
// not breakKey full match
breakKey: breakKey === void 0 ? passwordBreakKey : breakKey,
tokenMap: (ruleItem, inputToken, findIndex) => {
const match = ruleItem?.list[findIndex] === inputToken;
if (match) {
matchCount++;
return { ...ruleItem, isMatch: true };
}
return ruleItem;
},
});
return {
list: ruleList,
map: new Map(ruleList.map((e) => [e.name, e[passwordBreakKey]])),
matchCount,
// 想要獲取這個值,必須breakKey設(shè)置為空字符,如果提前退出會導(dǎo)致提前中止條件
// To get this value, breakkey must be set to null string
invaildWord: matchCount !== inputValue.length,
};
};
export const isMatchForceResultConfig = (
config: matchResultConfig,
matchNum: number,
inputValueLen: number,
) => {
return (
matchNum === config.matchNum &&
inputValueLen >= config.min &&
inputValueLen <= config.max &&
(config.need !== undefined ? config.need : true)
);
};
export type matchResultConfig = {
min: number;
max: number;
matchNum: number;
value: IpasswordForce;
need?: boolean;
back?: IpasswordForce;
};
export type IpasswordForce = false | 'high' | 'middle' | 'low';
流程就是合并規(guī)則,一個是默認(rèn)規(guī)則一個是自定義規(guī)則,如果自定義規(guī)則,那么就會覆蓋默認(rèn)規(guī)則。
從規(guī)則中,尋找規(guī)則數(shù)量最長的規(guī)則,因為等下我們遍歷的時候可以合并所有的規(guī)則,不管多少規(guī)則,其實遍歷數(shù)是區(qū)別不大的。
遍歷函數(shù)是個單獨的高階函數(shù),可以自定義處理內(nèi)部的邏輯,這時候,我們匹配到了之后對應(yīng)的規(guī)則,激活對應(yīng)規(guī)則的屬性,并累計匹配到的字符。
最后正常全部匹配到了就應(yīng)該中止遍歷,但是有一個情況是不能中止的,那就是需要判斷是否有非法字符。
最后這個函數(shù)把處理過的數(shù)據(jù)丟給上層組件,流程就是這樣
在數(shù)據(jù)拋出的過程中,有些場景可能需要對對應(yīng)規(guī)則的數(shù)據(jù)進(jìn)行特殊處理,但是如果是array結(jié)構(gòu)就很不方便,于是拋出的數(shù)據(jù)應(yīng)該分為list和map類型,上層應(yīng)用想要獲取對應(yīng)規(guī)則的情況可以map.get(規(guī)則名稱)來操作
constants.ts
export const specialList = ["~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "-", "/", ",", ".", "?", "<", ">", ";", ":", "[", "]", "{", "}", "|", "\\"];
export const numberList = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
export const wordList = ["q", "a", "z", "w", "s", "x", "e", "d", "c", "r", "f", "v", "t", "g", "b", "y", "h", "n", "u", "j", "m", "i", "k", "o", "l", "p", "Q", "A", "Z", "W", "S", "X", "E", "D", "C", "R", "F", "V", "T", "G", "B", "Y", "H", "N", "U", "J", "M", "I", "K", "O", "L", "P"];
其他
很多人可能會有疑問,一個代碼檢測器有必要搞這么復(fù)雜嗎,直接正則不好嗎。其實從實用角度來說,確實正則更方便點,但是有時候我們不想要循規(guī)蹈矩,或者想要手動編碼的快感,或者要從無聊中代碼獲得更多的可玩性等,于是編寫一個看起來挺復(fù)雜的代碼,不過把底層的封裝住,然后保留靈活性,在業(yè)務(wù)層里面盡量簡單點,其實也不是不可以試試的,但是也會在review的時候被懟,各位看官拷貝請注意哈,時間緊迫或者編碼能力不強(qiáng)的不建議使用本代碼,出問題本人概不負(fù)責(zé)。
總結(jié)
到此這篇關(guān)于react如何實現(xiàn)一個密碼強(qiáng)度檢測器的文章就介紹到這了,更多相關(guān)react密碼強(qiáng)度檢測器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題
這篇文章主要介紹了react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
react清空ant.design中表單內(nèi)容的方法實現(xiàn)
本文主要介紹了react清空ant.design中表單內(nèi)容的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12
react使用css module無法重寫bootstrap樣式問題及解決
這篇文章主要介紹了react使用css module無法重寫bootstrap樣式問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
React中useEffect 與 useLayoutEffect的區(qū)別
本文主要介紹了React中useEffect與useLayoutEffect的區(qū)別,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
react純函數(shù)組件setState更新頁面不刷新的解決
在開發(fā)過程中,經(jīng)常遇到組件數(shù)據(jù)無法更新,本文主要介紹了react純函數(shù)組件setState更新頁面不刷新的解決,感興趣的可以了解一下2021-06-06
react.js 父子組件數(shù)據(jù)綁定實時通訊的示例代碼
本篇文章主要介紹了react.js 父子組件數(shù)據(jù)綁定實時通訊的示例代碼,2017-09-09

