PHP正则表达式用于强密码验证

17

我在网上看到了以下的正则表达式。

(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$

只有当字符串:

   * contain at least (1) upper case letter
   * contain at least (1) lower case letter
   * contain at least (1) number or special character
   * contain at least (8) characters in length

我想知道如何将这个正则表达式转换为检查字符串以

* contain at least (2) upper case letter
* contain at least (2) lower case letter
* contain at least (2) digits
* contain at least (2) special character
* contain at least (8) characters in length

如果输入的密码至少包含2个大写字母、小写字母、数字和特殊字符,那么我就不需要限制8个字符的长度。

特殊字符包括:

`~!@#$%^&*()_-+=[]\|{};:'".,/<>?


2
请定义“特殊字符”。您是指仅限于 !@#$%^&*()-_=+[{]};:'",<.>/? 还是可能还有其他内容? - Crozin
已更新问题,包括特殊字符的字符集。 - Jason
4
你是否讨厌那些因为“安全原因”而限制密码复杂度的网站?你想设置一个来自句子的容易记忆的密码“.evmhcfcyK”,但最终只能在便签上写下“abcd1234”。 - Álvaro González
3个回答

66

我必须同意Alan的观点。如果现有的正则表达式太复杂了,为什么不尝试将其拆分成多个简单的步骤呢?

只需将其分解为可接受的简单步骤。您已经做到了这一点。

现在编写4个正则表达式来验证您的部分,添加基本逻辑到这4个正则表达式中并测量字符串的长度即可。

你更愿意调试哪一个:

(?=^(?:[^A-Z]*[A-Z]){2})(?=^(?:[^a-z]*[a-z]){2})(?=^(?:\D*\d){2})(?=^(?:\w*\W){2})^[A-Za-z\d\W]{8,}$(它实际上并不起作用...)

还是这个:

function valid_pass($candidate) {
   $r1='/[A-Z]/';  //Uppercase
   $r2='/[a-z]/';  //lowercase
   $r3='/[!@#$%^&*()\-_=+{};:,<.>]/';  // whatever you mean by 'special char'
   $r4='/[0-9]/';  //numbers

   if(preg_match_all($r1,$candidate, $o)<2) return FALSE;

   if(preg_match_all($r2,$candidate, $o)<2) return FALSE;

   if(preg_match_all($r3,$candidate, $o)<2) return FALSE;

   if(preg_match_all($r4,$candidate, $o)<2) return FALSE;

   if(strlen($candidate)<8) return FALSE;

   return TRUE;
}

为什么有些人觉得他们必须写一个没有人能理解的正则表达式,只是为了一步到位,这超出了我的理解范围...


好的,好的 - 如果你真的想要一个单一的正则表达式,请学习关于前瞻来验证您的规则。

这个"怪物"可以一次性完成你所要求的:

^                                        # start of line
(?=(?:.*[A-Z]){2,})                      # 2 upper case letters
(?=(?:.*[a-z]){2,})                      # 2 lower case letters
(?=(?:.*\d){2,})                         # 2 digits
(?=(?:.*[!@#$%^&*()\-_=+{};:,<.>]){2,})  # 2 special characters
(.{8,})                                  # length 8 or more
$                                        # EOL 

演示


我喜欢这个解决方案,它清晰明了,同时又相当简洁。+1 - andrhamm
@andrhamm 有没有办法将前两行连接起来?大写或小写字符不是大写和小写混合? - Byakugan
@Byakugan 请尝试 $r1='/[A-Za-z]/'; //大写或小写 - andrhamm
很好的答案,也帮助了我很多。但是如果r1...r4字符列表中没有任何内容会被视为无效字符,从而使候选变量无效,该怎么办?总结所有preg_match_all并查看是否等于长度? - Florian Mertens
@Florian Mertens:(当我要回答你的评论时,我意识到我有一个惊人的严重错误:在$r3中,序列“)-_”不是预期的!)只要测试满足要求,就可以有额外的字符。尝试一下。需要有2个小写字母;2个大写字母;2个在括号内的“特殊字符”;2个数字,并且长度必须大于或等于8。因此,密码“aaAA11!![[”将通过,即使我没有在$r3的定义中包括“[”或“]”。但是,“aaAA11![[”不会通过,因为“[”没有包含在r3中,也没有两个“特殊字符”。 - dawg

31

最好的方法是放弃使用该正则表达式,改为编写代码。所需的正则表达式会非常冗长和复杂,你在编写两个小时后也无法阅读它。相应的PHP代码可能会比较繁琐,但至少你能理解自己写的东西。

顺便说一下,这并不是对你的批评。正则表达式通常只适用于密码强度验证,但你的要求比通常更加复杂,用这种方式来完成并不值得。而且,你发帖的那个正则表达式真的很差。永远不要相信在网上找到的正则表达式,甚至包括任何代码或其他任何东西。 :-/


3
我同意,最好运行单独的检查而不是一个神奇的正则表达式。这样你可以轻松修改特定部分或添加/删除它们。个人认为强制使用强密码并不是一个良好的用户体验,那些基于JS的“密码强度”条更好,因为你既可以教育用户又可以对使用差密码的用户进行轻微惩罚,但如果他们真的想用一个强密码,他们仍然可以这样做。 - TravisO
好的好的..你们说服我了..我不会尝试正则表达式,因为它显然是无法阅读的。谢谢大家! - Jason
@TravisO:是的,我也喜欢那些密码强度条。 - Alan Moore
顺便提一下:为了阅读复杂的正则表达式,我使用RegexBuddy(http://www.regexbuddy.com/),它会给你一个树形结构,并解释每个部分的作用。你应该去看看。 - Radu Maris

16

如果你真的想使用正则表达式,可以尝试这个:

(?=^(?:[^A-Z]*[A-Z]){2})(?=^(?:[^a-z]*[a-z]){2})(?=^(?:\D*\d){2})(?=^(?:\w*\W){2})^[A-Za-z\d\W]{8,}$

一些解释:

  • (?=^(?:[^A-Z]*[A-Z]){2}) 检查两个 [^A-Z]*[A-Z] 的重复,这是一个由零个或多个大写字母后跟一个大写字母的序列。
  • (?=^(?:[^a-z]*[a-z]){2}) (与上面相同,只不过是小写字母)
  • (?=^(?:\D*\d){2}) (与上面相同,只不过是数字)
  • (?=^(?:\w*\W){2}) (与上面相同,只不过是非单词字符,但您可以将\W 更改为任何特殊字符的字符类)
  • ^[A-Za-z\d\W]{8,}$ 检查由所有其他字符类的字符组成的整个字符串的长度。

很好的答案,解释得很清楚,我会点赞,但是我不推荐你的答案。在这种情况下,我觉得它已经变得难以阅读了,标准代码可能是更好的选择,我只使用正则表达式是因为极端性能是一个问题,我需要检查数百万个密码。 - TravisO
1
实际上,如果性能是一个因素的话,这将是另一个不使用正则表达式的理由。否则我同意:如果你真的必须采用这种方法,那么用正则表达式解决方案是最终的选择。 - Alan Moore
+1 但是,与其提供一个长而难以阅读的本地格式正则表达式(以及单独的解释),不如从一开始就在自由空间模式下编写正则表达式,包括适当的缩进和详细的注释,这样做更好(也更省事)。另一个好处是,冗长的自我记录自由空间模式正则表达式在未来更易于维护。 - ridgerunner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接