密码强度校验:先行断言正则与通用库

May 20 2018 Node.js

在用户注册业务场景中,一般而言的用户密码强度:密码至少8个字符,包括1个大写字母,1个小写字母和1个数字或特殊字符。本文原本转载来自css88的一篇整理,但是其内容只是简单说明了几种验证密码强度正则的写法,比如说 /^(?=.*\d)(?=.*[a-zA-Z]).{6,20}$/ ,这里用了「先行断言」,之前自己对此理解不太准确,本文就是附带讲解这个概念来一步一步说明密码强度正则的写法。

先行断言

先行断言(Positive look-ahead)的语法形式为 x(?=y),意思就是 x 只有在 y 前面才匹配,并且 y 不会被计入返回结果。

如果要匹配一个 70% 中的数字,就可以写成 /\d+(?=%)/

先行断言很容易让人误解,比如说上面举例的密码强度表达式(/^(?=.*\d)(?=.*[a-zA-Z]).{6,20}$/)中的先行断言前面并没有任何字符,它其实只是匹配某些位置不占用字符,所以匹配返回的内容是不包括先行断言内的内容的。

下面使用 JavaScript 具体说明:

1
var res = /(?=\d)[a-z]/.test("1h")

那么res 的返回结果是?肯定有人说是 true 但是正确结果为 false

接下来来分析一下,想象一个指针在字符串 1h 上移动,那么根据上面例子的正则,指针会在 1h 之前,用下划线指代指针的话,指针的位置就是 _1h 的,但是 (?=\d)[a-z] 要求后面的字符是不是数字而是小写字母,所以就匹配不了。

那么,我们稍微修改一下,现在就匹配了。也就是说断言不消耗字符,如果找不到断言就不匹配。

1
/(?=\d)\d[a-z]/.test("1h")

那么再说明密码强度的正则就简单了。

1
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{6,}$/

在断言中加入 . 代表任意字符,那么就是匹配整个密码字符串。

密码强度库

除了正则之外,还有一个业界通用的密码强度库 Dropbox 的 zxcvbn

库名字很好记就是键盘字符第三排的字母,另外根据统计取这样密码的用户很多。

zxcvbn 覆盖了所有主流的语言,包括 JS PHP Python 等,前端也可以直接引入,不过我并不推荐,因为包太大了,足有 800K。如果前端使用的话,最好还是使用正则。