Java中的正则表达式用于密码验证

119
我正在为Java应用程序创建一个用作配置参数的密码验证正则表达式。
正则表达式是:
^.*(?=.{8,})(?=..*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$

密码策略如下:

  • 至少8个字符

  • 包含至少一个数字

  • 包含至少一个小写字母和一个大写字母

  • 包含至少一个特殊字符 (@#%$^ 等)

  • 不包含空格、制表符等

我缺失的是第5点。我无法让正则表达式检查空格、制表符、回车等等。

有人可以帮忙吗?


4
密码规则不好。请参考参考 - 密码验证获取更多信息。 - ctwheels
17个回答

358

试试这个:

^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$

解释:

^                 # start-of-string
(?=.*[0-9])       # a digit must occur at least once
(?=.*[a-z])       # a lower case letter must occur at least once
(?=.*[A-Z])       # an upper case letter must occur at least once
(?=.*[@#$%^&+=])  # a special character must occur at least once
(?=\S+$)          # no whitespace allowed in the entire string
.{8,}             # anything, at least eight places though
$                 # end-of-string

每个规则都是独立的“模块”,因此很容易添加、修改或删除单个规则。

(?=.*[xyz])结构会匹配整个字符串(.*),并回溯到第一个出现[xyz]可以匹配的位置。如果找到了[xyz]它会成功,否则它会失败。

另一种选择是使用懒惰限定符:(?=.*?[xyz])。对于密码检查,这几乎没有任何影响,在更长的字符串中,它可能是更有效的变体。

当然,最有效的变体(但最难阅读和维护,因此最容易出错)是(?=[^xyz]*[xyz])。对于这个长度和目的的正则表达式,我不建议使用它,因为它没有真正的好处。


17
在Java的字符串中,反斜杠必须被转义。尝试使用\\s。这是Java的要求,而不是正则表达式的要求。 - Tomalak
4
此答案已添加到Stack Overflow 正则表达式 FAQ,位于“常见验证任务”下。 - aliteralmind
1
@shA.t 这是同样的事情。我只是尝试保持 (?=...) 模式,以便它与表达式的其余部分相匹配。 - Tomalak
2
@shA.t 无论你使用“仅包含非空格字符”((?=\S+$))还是“不包含空格字符”((?!.*\s)),都是个人偏好的问题。使用你更喜欢的即可 :) - Tomalak
1
我注意到给定的表达式也允许包含像~和|这样的字符的密码。我认为该表达式应该允许以下字符:@#$%^&+=。 - Stephan
显示剩余13条评论

62

使用正则表达式的简单示例

public class passwordvalidation {
    public static void main(String[] args) {
      String passwd = "aaZZa44@"; 
      String pattern = "(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}";
      System.out.println(passwd.matches(pattern));
   }
}

解释:

  • (?=.*[0-9]) 必须至少出现一次数字
  • (?=.*[a-z]) 必须至少出现一个小写字母
  • (?=.*[A-Z]) 必须至少出现一个大写字母
  • (?=.*[@#$%^&+=]) 必须至少出现一个特殊字符(@#$%^&+=)
  • (?=\\S+$) 整个字符串中不能有空格
  • .{8,} 至少8个字符

5
如果有人在寻找具体的解释,.{5,10}代表最少5个字符,最多10个字符。 - abhy
@iabhi,我一直在寻找这个。谢谢你。 - Akshatha S R
我正在尝试在 char 数组上实现正则表达式,而不是在 String 上实现,因为这样更安全。但是如何将正则表达式应用于 char 数组呢? - AgentM
这个程序还可以接受通常不允许的字符,比如“€”或“§”(例如:“1q@W€°§ß”)。你应该将最后的“.{8,}”替换为允许的字符列表,例如“[@#$%^&+=a-zA-Z0-9]{8,}”。 - Sampisa

14

之前给出的所有答案都使用了相同(正确的)技巧,为每个需求使用单独的前瞻。但它们包含一些低效和一个潜在的巨大错误,具体取决于实际使用密码的后端。

我将从已接受的答案中提取正则表达式:

^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$

首先,由于Java支持\A\z,我更喜欢使用它们来确保整个字符串得到验证,而不受Pattern.MULTILINE的影响。这不会影响性能,但可以避免在重复使用正则表达式时出现错误。

\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z

通过在允许字符的简写 \S 上放置变量量词 {8,},可以一次性完成检查密码是否包含空格和检查其最小长度的操作:

\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z
如果提供的密码包含空格,则所有检查都将进行,但最终检查会因为空格而失败。可以通过使用\S替换所有点来避免这种情况:
\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z

仅当您确实希望允许任何字符时,才应使用点。否则,请使用(非)字符类将您的正则表达式限制为仅允许那些真正允许的字符。虽然在这种情况下几乎没有区别,但是在更适当的情况下不使用点是一个很好的习惯。我看到有太多的因为开发人员懒得使用比点更合适的东西而导致灾难性回溯的情况

由于在密码的前半部分找到合适的字符的机会很大,所以使用懒惰量词可能更有效:

\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z

但现在是真正重要的问题:没有任何答案提到原始问题似乎是由一个使用ASCII字符集思维的人编写的。但在Java中,字符串是Unicode编码。密码中是否允许非ASCII字符?如果允许,只有ASCII空格被禁止,还是应该排除所有Unicode空白符?

默认情况下,\s仅匹配ASCII空白符,因此它的反义词\S匹配所有Unicode字符(无论是否为空格)和所有非空白ASCII字符。如果允许Unicode字符但不允许Unicode空格,则可以指定UNICODE_CHARACTER_CLASS标志,使\S排除Unicode空格。如果不允许Unicode字符,则可以使用[\x21-\x7E]替代\S来匹配所有不是空格或控制字符的ASCII字符。

这就带来了下一个潜在问题:我们是否想允许控制字符?撰写正确的正则表达式的第一步是明确指定您要匹配的内容和不匹配的内容。唯一100%技术上正确的答案是,问题中的密码规范不明确,因为它没有说明是否允许某些字符范围,例如控制字符或非ASCII字符。


13

如果可能的话,不应该使用过于复杂的正则表达式,因为它们:

  • 难以阅读(至少对于除自己之外的其他人来说)
  • 难以扩展
  • 难以调试

虽然使用许多小正则表达式可能会带来一些性能开销,但以上所述优点很容易超越它。

我会这样实现:

bool matchesPolicy(pwd) {
    if (pwd.length < 8) return false;
    if (not pwd =~ /[0-9]/) return false;
    if (not pwd =~ /[a-z]/) return false;
    if (not pwd =~ /[A-Z]/) return false;
    if (not pwd =~ /[%@$^]/) return false;
    if (pwd =~ /\s/) return false;
    return true;
}

从安全角度来看,强制使用更长的密码、防止使用众所周知的密码(如12345和pass=user),而不是让密码变得超级复杂和难以记忆,这样做要好得多。 - Martin Rauscher
我喜欢你上面的方法。谢谢你! - Thomas Lang

5

感谢所有答案,基于它们但扩展了特殊字符:

@SuppressWarnings({"regexp", "RegExpUnexpectedAnchor", "RegExpRedundantEscape"})
String PASSWORD_SPECIAL_CHARS = "@#$%^`<>&+=\"!ºª·#~%&'¿¡€,:;*/+-.=_\\[\\]\\(\\)\\|\\_\\?\\\\";
int PASSWORD_MIN_SIZE = 8;
String PASSWORD_REGEXP = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[" + PASSWORD_SPECIAL_CHARS + "])(?=\\S+$).{"+PASSWORD_MIN_SIZE+",}$";

单元测试:

在这里输入图片描述


1
密码要求:
  • Password should be at least eight (8) characters in length where the system can support it.
  • Passwords must include characters from at least two (2) of these groupings: alpha, numeric, and special characters.

    ^.*(?=.{8,})(?=.*\d)(?=.*[a-zA-Z])|(?=.{8,})(?=.*\d)(?=.*[!@#$%^&])|(?=.{8,})(?=.*[a-zA-Z])(?=.*[!@#$%^&]).*$
    

我测试过了,它可以正常工作


1
这个检查每个特殊字符:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=\S+$).*[A-Za-z0-9].{8,}$

1

如果有人对每种类型的字符的最低要求感兴趣,我建议在Tomalak的答案基础上进行以下扩展:

^(?=(.*[0-9]){%d,})(?=(.*[a-z]){%d,})(?=(.*[A-Z]){%d,})(?=(.*[^0-9a-zA-Z]){%d,})(?=\S+$).{%d,}$

请注意,这是一个格式化字符串而不是最终的正则表达式模式。只需用所需的最小出现次数替换%d,以获取数字、小写字母、大写字母、非数字/字符和整个密码(依次)的要求。最大出现次数不太可能(除非您想要最大为0,从而有效地拒绝任何此类字符),但也可以很容易地添加。请注意每种类型周围的额外分组,以便最小/最大约束允许非连续匹配。这对于我们可以集中配置所需每种字符的数量,然后使网站以及两个不同的移动平台提取该信息以构建基于上述格式化字符串的正则表达式模式的系统非常有帮助。

1

为您准备了带参数的Java方法

只需复制粘贴并设置所需参数即可。

如果您不想要一个模块,只需像我一样对特殊字符添加注释或"if"语句。

//______________________________________________________________________________
/**
 * Validation Password     */
//______________________________________________________________________________
private static boolean validation_Password(final String PASSWORD_Arg)    {
    boolean result = false;
    try {
        if (PASSWORD_Arg!=null) {
            //_________________________
            //Parameteres
            final String MIN_LENGHT="8";
            final String MAX_LENGHT="20";
            final boolean SPECIAL_CHAR_NEEDED=true;

            //_________________________
            //Modules
            final String ONE_DIGIT = "(?=.*[0-9])";  //(?=.*[0-9]) a digit must occur at least once
            final String LOWER_CASE = "(?=.*[a-z])";  //(?=.*[a-z]) a lower case letter must occur at least once
            final String UPPER_CASE = "(?=.*[A-Z])";  //(?=.*[A-Z]) an upper case letter must occur at least once
            final String NO_SPACE = "(?=\\S+$)";  //(?=\\S+$) no whitespace allowed in the entire string
            //final String MIN_CHAR = ".{" + MIN_LENGHT + ",}";  //.{8,} at least 8 characters
            final String MIN_MAX_CHAR = ".{" + MIN_LENGHT + "," + MAX_LENGHT + "}";  //.{5,10} represents minimum of 5 characters and maximum of 10 characters

            final String SPECIAL_CHAR;
            if (SPECIAL_CHAR_NEEDED==true) SPECIAL_CHAR= "(?=.*[@#$%^&+=])"; //(?=.*[@#$%^&+=]) a special character must occur at least once
            else SPECIAL_CHAR="";
            //_________________________
            //Pattern
            //String pattern = "(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}";
            final String PATTERN = ONE_DIGIT + LOWER_CASE + UPPER_CASE + SPECIAL_CHAR + NO_SPACE + MIN_MAX_CHAR;
            //_________________________
            result = PASSWORD_Arg.matches(PATTERN);
            //_________________________
        }    

    } catch (Exception ex) {
        result=false;
    }

    return result;
}        

1
使用Passay库,它是一个功能强大的API。

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