正则表达式 - 仅限一个特殊字符

3
我目前正在使用我创建的正则表达式来处理密码。
它必须遵守以下条件:
  • 8个字符
  • 至少一个大写字母(A-Z)
  • 至少一个小写字母(a-z)
  • 至少一个数字(0-9)
  • 其中一个特殊字符: .,:;'!@#$%^&*_+=|(){}[?-]/\
  • 禁止使用以下字符: <>`
以下是正则表达式:
(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\])(?!.*[<>`]).{8}

一切正常,但现在我希望它只接受一个特殊字符。我搜索并尝试了很多方法(例如在组的末尾使用{1}),但根本不起作用!结果,像Aa1;;aaa这样的字符串仍然可以匹配。
请问有人能告诉我怎么做吗?

4
顺便说一句:请不要限制用户。有些用户不喜欢被强制使用难以记住的密码。如果你想的话可以添加一个“您的密码太弱”的警告,但是完全禁止使用弱密码只会让人感到沮丧。(而且,说像“somelargesentencethatishardtobruteforce”这样的密码是弱密码,简直是愚蠢的。) - Cássio Renan
2
限制用户这样做会降低安全性,因为它拒绝强密码并使密码生成器难以使用。 - Tom
那是一个又长又可怕的正则表达式。你有没有考虑分别应用验证标准,这样当你在6个月后回来时,至少有机会理解它? - Sobrique
1
也许这会更短: (?=[^\W_]*[\W_][^\W_]*$)。但我强烈建议您将正则表达式分开,这样您可以向用户发出警告,说明他们的密码失败的原因(例如:“你需要至少一个数字”,“不能有两个或更多的特殊字符”,...)。 - user4227915
2个回答

2

实际上,我不会这样做。首先,繁琐的密码规则并不是一个好主意。它们会降低安全性,因为它们会减少密钥空间。

但更根本的是,只有一个正则表达式来支配所有事情并不是一个好的设计策略。您现有的正则表达式已经足够长且复杂,以至于维护程序员将不得不花费一些时间来弄清楚它的作用,如果它在未来发生变化...您将再次遇到这个头疼问题。

因此,我提供一个替代方案-将您的正则表达式拆分开来,单独应用验证标准,并提供消息以说明失败原因。类似这样的实现(使用您正在使用的任何语言):

#!/usr/bin/env perl
use strict;
use warnings;

while ( <DATA> ) {
    chomp;
    my $fail = 0; 
    print "Testing: \"$_\"\n"; 

    if (not m/.{8}/) { 
        print "Rejected because less than 8 chars\n"; 
        $fail++;
    }
    if ( not m/[A-Z]/ ) { 
         print "Rejected - needs at least one upper case character\n";
         $fail++;
    }
    if ( not m/\d/ ) { 
        print "Rejected - needs at least one digit\n"; 
         $fail++;
    }
    if ( my @matches =  m/([.,:;\'!@#$%^&*_+=|(){}[?\-\]\/\\])/g ) {
        if ( @matches != 1 ) { 
                print "Rejected - exactly one special character, not more\n";
                $fail++; 
        }
    } 
    else {
        print "Rejected - needs a special character\n";
        $fail++; 
    }
    if ( m/[\<\>\`]/ ) { 
        print "Rejected - \"<>`\" not permited\n";
        $fail++; 
    }
    if ( not $fail ) { 
        print "$_ is OK\n";
    }
}

__DATA__
Fish1234!
!!moooMoo33
helloMum

这将会得到:
Testing: "Fish1234!"
Fish1234! is OK
Testing: "!!moooMoo33"
Rejected - exactly one special character, not more
Testing: "helloMum"
Rejected - needs at least one digit
Rejected - needs a special character

(我要注意的是 - Fish1234!即使它符合您的规则,也不是一个强大的密码)

2
这句话的意思是:关于什么?

^(?=.{8}$)(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?!.*[<>`])([^.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\]*)[.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\](?1)$

演示

它使用前瞻检查长度,然后确保它只有一个特殊字符,并且在其之前和之后有不同的内容

(?1) 是对第一组模式的引用,如果您的工具不支持,可以将其替换为 [^.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\]*

演示

或者,保留原始语法:

(?=.*[A-Z]) (?=.*[a-z]) (?=.*[0-9]) (?!.*[<>`]) (?=[^.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\]* [.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\] [^.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\]*$ ) .{8}$

这是紧凑版:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?!.*[<>`])(?=[^.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\]*[.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\][^.,:;'!@#$%^&*_+=|(){}[?\-\]\/\\]*$).{8}$

演示


我认为这是一个非常好的例子,说明了正则表达式非常强大,但并不适用于每个任务! - Sobrique
1
@Sobrique 确实回答了原帖的问题,但正如上面的评论所说:限制密码这么多是个坏主意;我曾经亲身经历过在注册页面卡住,不知道出了什么问题 => 结果原来是密码太复杂了 :S - Enissay
同意 - 的确如此。正则表达式是一种非常强大的工具。但在我看来,它并不适合这个特定的工作。 - Sobrique
1
谢谢大家的回答,我很感激!还要感谢Enissay,我会采用你的代码,它完成了工作!一开始我没有说,但这个正则表达式将用于内部网站,并且仅由公司的AD用户更改密码使用。 - dattik

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