最近收到产品需求,需要对原邮件密码规则进行修改。
先由客户端修改密码规则,服务端后续才进行更新。
具体需求如下:
1 | 1.新密码必须包含大写字母、小写字母、数字、特殊字符任意3种组合 |
原有密码校验规则与新规则区别较大,且历史代码写得比较混乱,因此打算重新设计。
匹配规则还是挺复杂的,一步步来。
1 | 新密码必须包含大写字母、小写字母、数字、特殊字符任意3种组合 |
首先根据这条,网上很多正则表达式是这种:
1 | "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}" |
但是这种是至少包含至少一个大写字母,一个小写字母,一个数字和一个特殊字符,正则表达式里只有或,并不好区分”任意”这种说法。
因此实现思路是,需要分别设计大写字母、小写字母、数字、特殊字符的判断,再通过Pattern.match(regex, password)判断符合种数,大于3才表示匹配此规则。
其他特殊字符太多,无法进行排除,考虑换个方式,这样写可以设定取出以下字符:
1 | [A-Za-z0-9_~@#$]+ |
但是这样写并无法排除穿插在其中的特殊字符,比如”!22A123B2!##@$#@!”,这三个感叹号会分割匹配结果,并没有排除掉感叹号情况。
此时需要通过正向预查表达式(?=exp),它断言自身出现的位置的后面能匹配表达式exp。
即必须包含exp,只要将大写字母表达式写在exp即可。
匹配单个大写字符:
1 | [A-Z] |
匹配任意字符多次,包含大写字符:
1 | .*[A-Z] |
于是包含大写的正向预查表达式出来了:
1 | (?=.*[A-Z]) |
但是在正则表达式测试网站测试,这个表达式并不能匹配出任何字符,原因他只是个断言,所以最终得出的包含大写字母的表达式如下:
1 | ^(?=.*?[A-Z])[A-Za-z0-9_~@#$]+$ |
类似可分别得出包含小写字母、数字、特殊字符的表达式。
长度至少为8位,正则表达式:
1 | {8,} |
但是由于第一条规则的正则表达式已经进行了拆解,所以通过Java代码实现。
不能包含3位以上相同字符:
1 | ([0-9a-zA-Z.~!@$_])\1{2} |
“\1”代表第一个圆括号表达式的值,”{2}”代表至少匹配两次。
为了加速匹配还可以一开始进行字符集判断,含有其他特殊字符的就不进行下述的排查:
1 | ^[A-Za-z0-9.~!@$_]+$ |
于是密码匹配规则伪代码出来了:
1 | public int passwordMatching(String password, String account) { |
Done.