用正则表达式进行复杂密码校验
来源:互联网 发布:mac输入法下载安装 编辑:程序博客网 时间:2024/06/05 09:14
基本结构
复杂密码要求各式各样,但不外乎下面几类:
- 包含大写字母、小写字母、数字、特殊符号中的至少多少种
- 至少包含多少个数字,或者至少包含多少个字母
- 长度在多少到多少之间
其他情况欢迎留言补充
案例 1
密码包含大写字母、小写字母、数字、特殊符号中的至少三类,且长度在8到20之间。
正则表达式
^(?!([A-Z]*|[a-z]*|[0-9]*|[!-/:-@\[-`{-~]*|[A-Za-z]*|[A-Z0-9]*|[A-Z!-/:-@\[-`{-~]*|[a-z0-9]*|[a-z!-/:-@\[-`{-~]*|[0-9!-/:-@\[-`{-~]*)$)[A-Za-z0-9!-/:-@\[-`{-~]{8,20}$
可读性基本为0,请慎重考虑
伪正则
为了方便理解,定义下面的符号进行替换,简化为下面的“伪正则”。注意,“伪正则”不能直接当做正则表达式来用。
- 用 A 表示大写字母,即
A-Z
- 用 a 来表示小写字母,即
a-z
- 用 0 来表示数字,即
0-9
- 用 @ 来表示特殊字符,即ASCII码表上的 !到/,:到@,[到`,{到~。在Java中可用\p{Punct}表示。
^(?!([A]*|[a]*|[0]*|[@]*|[Aa]*|[A0]*|[A@]*|[a0]*|[a@]*|[0@]*)$)[Aa0@]{8,20}$
分析
思路:用零宽度前向负断言(zero-width negative lookahead)来排除只包含一类和只包含两类字符的简单密码
分解:
^
: 匹配行首(?! ... )
: 表示零宽度前向负断言(zero-width negative lookahead),断言紧接着后面的内容一定不能出现。([A]*|[a]*|[0]*|[@]*|[Aa]*|[A0]*|[A@]*|[a0]*|[a@]*|[0@]*)$
: 表示字符串全部由任意一类字符组成,或者全部由任意两类字符组成。这里列举了所有的组合情况,一类的四种组合和两类的六种组合。和前面的负断言结合,含义发生反转,即表示整个字符串不能仅由一类字符组成,也不能仅由两类字符组成。[Aa0@]{8,20}
: 匹配整个字符串,限制只能出现这四类字符,且长度在8到20之间$
: 匹配行尾
性能
用Java测试,经过充分预热,使用正则表达式的性能差了近30倍。
测试代码:
public class Test { private static final String Regexp = "^(?!([a-z]*|[A-Z]*|[0-9]*|[\\p{Punct}]*|[a-zA-Z]*|[a-z0-9]*|[a-z\\p{Punct}]*|[A-Z0-9]*|[A-Z\\p{Punct}]*|[0-9\\p{Punct}]*)$)[a-zA-Z0-9\\p{Punct}]{8,20}$"; private static java.util.Random random = new java.util.Random(); public static void main(String[] args) throws Exception { final int sampleSize = 1_000_000; String[] src = new String[sampleSize]; for (int i = 0; i < sampleSize; i++) { src[i] = randomString(); } for (String s : src) { // 预热 f1() 和 f2() f1(s); f2(s); } long nano = System.nanoTime(); for (String s : src) { f1(s); } System.out.printf("正则表达式 cost %10d 纳秒%n", -(nano - (nano = System.nanoTime()))); for (String s : src) { f2(s); } System.out.printf("不使用正则 cost %10d 纳秒%n", -(nano - (nano = System.nanoTime()))); } // 使用正则表达式 private static boolean f1(String s) { return s.matches(Regexp); } // 不使用正则表达式 private static boolean f2(String s) { int len = s.length(); if (len < 8 || len > 20) return false; int flag = 0; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c >= 'a' & c <= 'z') { // 包含a-z flag |= 0b0001; } else if (c >= 'A' & c <= 'Z') { // 包含A-Z flag |= 0b0010; } else if (c >= '0' & c <= '9') { // 包含0-9 flag |= 0b0100; } else if ((c >= '!' & c <= '/') || (c >= ':' & c <= '@') || (c >= '[' & c <= '`') || (c >= '{' & c <= '~')) { // 包含特殊字符 flag |= 0b1000; } else { return false; } } return Integer.bitCount(flag) >= 3; } // 构建长度为4-24的随机字符串,由字母、数字、特殊符号组成 private static String randomString() { int len = random.nextInt(21) + 4; // 4 - 24 char[] chars = new char[len]; for (int i = 0; i < len; i++) { // 按照ASCII顺序 从'~'到'!'包括了所有的数字、字母和特殊字符 chars[i] = (char) (random.nextInt('~' - '!' + 1) + '!'); } return new String(chars); }}
输出结果:
正则表达式 cost 3725218499 纳秒不使用正则 cost 131117117 纳秒
性能差距近30倍。
案例 2
密码包含数字和字母,以及@#$三种符号,总长度6-8,必须包含2位及以上的字母,必须包含数字。
正则表达式
^(?=(.*[A-Za-z]){2})(?=.*[0-9])[A-Za-z0-9@$#]{6,8}$
分析
思路:使用零宽度正向正断言(zero-width positive lookahead)来限制数字和字母个数。
分解:
^
: 匹配行首(?= ... )
: 表示零宽度正向正断言(zero-width positive lookahead),断言后面一定要出现的内容。(.*[A-Za-z]){2}
: 匹配任意位置出现的两次字母。只需要匹配到两次即可,不需要匹配到末尾。(?=.*[0-9])
: 这个断言表示字符串中一定要出现数字,不管在什么位置。同样,匹配到一次即可,不需要匹配到末尾。[A-Za-z0-9@$#]{6,8}
: 匹配整个字符串,限制只能出现数字和字母,以及@#$三种符号,且长度在6到8之间$
: 匹配行尾
阅读全文
1 0
- 用正则表达式进行复杂密码校验
- 利用正则表达式进行校验
- JAVA正则表达式校验密码实战实例
- JAVA中校验密码的正则表达式
- 校验密码强度的js正则表达式
- 用正则表达式对qlineedit的输入参数进行校验
- 用struts框架+正则表达式对数据进行校验
- MySQL-正则表达式进行复杂匹配
- js正则表达式进行格式校验
- 正则表达式对qq号码进行校验
- 正则表达式进行大陆手机号码规则校验
- 正则表达式-对邮件地址进行校验
- 用正则表达式作校验
- 用正则表达式作校验
- 用正则表达式作校验
- 用正则表达式校验邮箱
- MFC利用正则表达式进行密码匹配
- JS实现用户名,密码正则表达式的校验
- Hyperledger Fabric1.0架构概览
- 如何使用控制台安装mysql-5.6.17-win32?
- linux dump_stack
- 遮蔽元素和去除遮蔽
- change()事件及val()/html()方法不会触发change事件
- 用正则表达式进行复杂密码校验
- Android studio更新到2.3后一直building
- docker+nexus+jenkins+java+maven
- 【Linux】详解CRC校验
- 守护进程(精灵进程)
- 利用mycat实现mysql读写分离
- Android学习之活动的生命周期
- j-link出现CPU could not be halted;no sw-dp found;Too many errors to display
- Huffman代码调试