【问题标题】:Validate password using regex使用正则表达式验证密码
【发布时间】:2025-12-01 21:45:01
【问题描述】:

我正在编写一个正则表达式来验证密码。

以下是我要介绍的密码策略:

  1. 密码只能包含数字、字母和特殊字符。

  2. 密码最小长度为10,密码最大长度为32。

  3. 同一字符不应连续出现 10 次或更多次。

  4. 第一个字符不能是特殊字符。

  5. 至少需要 2 个字符类别。(字母、数字或特殊字符)

  6. 允许使用特殊字符 - !#+,-./:=@_

满足除第 5 点外的前 4 个条件的正则表达式:

^(?!.*(.)\1{7})[A-Za-z0-9][\w!#+,./:=@-]{7,23}

如何在 java 中同时验证所有策略?

【问题讨论】:

  • 反击这些要求。它们使人们难以记住但计算机容易猜到的密码。特别是,垃圾所需的 2 个类和最大长度。

标签: regex perl


【解决方案1】:

不是为有效密码创建一个返回 true 的子程序,相反的子程序可以改为返回零个或多个错误消息。

优点当然是可以将错误消息显示给用户,以便准确指出哪些规则被违反(如果有的话)。

sub pwerr {
  local $_=pop;
  my $s='!#+,-./:=@_'; #allowed special chars
  grep $_,
  /^[a-z\d$s]+$/i        ? 0 : "Password must be just nums, letters and special chars $s",
  length()>=10           ? 0 : "Minimum length of the password is 10",
  length()<=32           ? 0 : "Maximum length of the password is 32",
  !/(.)\1{9}/            ? 0 : "Same char 10 or more in a row",
  /^[a-zA-Z0-9]/         ? 0 : "First character can not be special character",
  1</[a-z]/i+/\d/+/[$s]/ ? 0 : "At least 2 char classes of letters, numbers or special $s";
}

use strict; use warnings; use Test::More tests => 7;
sub _test_string { join("+",map{/^(\S+)/;$1}pwerr(shift()))||undef }
is(_test_string($$_[0]), $$_[1]) for map[split],grep/\w/,split/\n/,q(
1A!~                               Password+Minimum
abc                                Minimum+At
abcd12345-
abcd12345.
-abcd12345                         First
abcd4444444444                     Same
abcd12345.abcd12345.abcd12345.xyz  Maximum
);

【讨论】:

    【解决方案2】:
    print "Enter your password please: ";
    $p=<STDIN>;chomp $p;
    
    if ( $p =~ /(?!^[\s!#+,-./:=@_])(?=^[\w!#+,-./:=@]{10,32}$)(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!#+,-./:=@_])(?!.*(.)\1{9,}).{10,32}/ ) {print "Welcome"; f=1}
    

    【讨论】:

    • 请添加一些解释。在 SO 中不鼓励仅代码答案
    【解决方案3】:

    最好的方法是不使用正则表达式。

    具有单独条件的子程序更容易阅读和维护:

    sub is_password_valid {
        my ($pw) = @_;
        $pw =~ m{[^a-zA-Z0-9!\#+,\-./:=\@_]}
            and return 0;
        length($pw) >= 10 && length($pw) <= 32
            or return 0;
        $pw =~ /(.)\1{9}/s
            and return 0;
        $pw =~ /^[a-zA-Z0-9]/
            or return 0;
        ($pw =~ /[a-zA-Z]/ + $pw =~ /[0-9]/ + $pw =~ /[^a-zA-Z0-9]/) >= 2
            or return 0;
        return 1;
    }
    

    或者,因为这基本上只是一个大条件:

    sub is_password_valid {
        my ($pw) = @_;
        return
            $pw !~ m{[^a-zA-Z0-9!\#+,\-./:=\@_]} &&
            length($pw) >= 10 &&
            length($pw) <= 32 &&
            $pw !~ /(.)\1{9}/s &&
            $pw =~ /^[a-zA-Z0-9]/ &&
            ($pw =~ /[a-zA-Z]/ + $pw =~ /[0-9]/ + $pw =~ /[^a-zA-Z0-9]/) >= 2
        ;
    }
    

    如果这不是家庭作业练习的玩具验证器,您应该更改您的要求。用正则表达式“验证”密码是没有意义的。

    您应该改为要求最小长度,具有更高的最大长度(可能 255 个字符左右),并且不限制使用的字符集。

    如果您想防范弱密码,请检查haveibeenpwned 并让密码破解者(例如hashcat)尝试一下。

    【讨论】:

    • 我真的不喜欢在这种情况下使用... and/or return 0 而不是if (...) { return 0 }。 (否则,我同意根据要求使用一个语句比尝试将它们全部放在一个正则表达式中更好)
    • return 0 if ...;return 0 unless ...; 更好,也更惯用的符号。
    • @Shawn 我不同意。测试更重要,所以它是第一位的。 return 0 相比之下就很无聊了。 (另见:perlstyle
    • 看,我认为在这种情况下,返回错误的重要部分。
    • 在 set-a-password-UI 中尝试密码破解程序不太可能。有一些熵/黑名单检查库会更好,例如metacpan.org/pod/Data::Password::zxcvbn(javascript zxcvbn 库的 perl 端口;参见 lowe.github.io/tryzxcvbn