密码验证的正则表达式

60

我目前使用此正则表达式来检查字符串是否符合一些条件。

这些条件是:字符串长度必须在8到15个字符之间;字符串中必须包含至少一个数字;字符串中必须包含至少一个大写字母;字符串中必须包含至少一个小写字母。

(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]{8,15})$

它基本上工作良好,但不允许特殊字符。如果需要修改此正则表达式以允许特殊字符,请帮忙修改。谢谢!


15
我希望人们停止对密码施加愚蠢的限制。这让人感到烦恼,同时也降低了密码的安全性。 - Oliver Charlesworth
5
如果您不强制执行此类限制,大多数用户倾向于创建非常简单且很容易被破解的密码 :)。 - desi
2
@Oli 我基本上同意,但我认为8个字符的最小限制是有效的约束条件。整个大写/小写/数字/特殊/长子限制有点过头了。 - josh.trow
2
@desi:但是通过这种方式强制要求,最终会导致人们创建无法记忆的密码,于是他们会把它们写在某个地方。 - Oliver Charlesworth
5
@Oli:社交工程一直是个问题。如果密码没有任何限制,你可以将相关名称等放在你的密码列表顶部进行检查;在其他情况下,你至少需要有物理接触来找到存储密码的纸条、钱包等。总的来说,包含合理限制的密码(例如,多个字符类)比没有限制的密码更安全。我同意,它们也不完美,但实际上,什么是完美的呢? - Daniel Hilgarth
显示剩余6条评论
12个回答

104
这里似乎存在很多混淆。到目前为止,我看到的答案没有正确执行1 +数字/ 1+小写/ 1+大写字母规则,这意味着像abc123、123XYZ或AB&^#*这样的密码仍然会被接受。仅防止所有小写字母、所有大写字母或所有数字是不够的;您必须强制出现至少每种类型中的一种。
尝试以下操作:
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,15}$

如果您还想要至少一个特殊字符(这可能是个好主意),请尝试以下代码:
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,15}$

.{8,15} 可以更加严格,如果您希望的话(例如,您可以将其更改为 \S{8,15} 来禁止空格),但请记住,这样做会降低您密码方案的强度。

我已经测试了这个模式,它按预期工作。


编辑: 一个小提示,最简单的方法是使用3个单独的正则表达式和字符串的Length属性。这样做也更容易阅读和维护,因此如果您有选择,就应该这样做。但是,如果这是用于标记中的验证规则,则可能只能使用单个正则表达式。


2
起始和结尾的“/”只是为了强调这是正则表达式代码。如果您使用(例如).NET,则不应包括它们。 - Justin Morgan
更新:为避免混淆,已删除前斜杠。因为这个问题与.NET有关。 - Justin Morgan
2
如果你想要超过8个字符,可以将.{8,15}改为.{8,}。 - Abdurrahman Mubeen Ali
你说的第一种尝试允许像句点等字符。请提供一个只允许字母数字(即数字,小写和大写字母)的选项。 - Olumide
为什么我们需要使用 ^ 和 $? 我测试了一下没有 ^$ 的正则表达式,结果也很好。 - Chuck Lu
1
@ChuckLu - 没有 ^ 和 $,它就无法强制执行 15 个字符的最大限制。 - Justin Morgan

49

正则表达式是否比更明显的方法更容易/更好地强制实施简单约束条件?

static bool ValidatePassword( string password )
{
  const int MIN_LENGTH =  8 ;
  const int MAX_LENGTH = 15 ;

  if ( password == null ) throw new ArgumentNullException() ;

  bool meetsLengthRequirements = password.Length >= MIN_LENGTH && password.Length <= MAX_LENGTH ;
  bool hasUpperCaseLetter      = false ;
  bool hasLowerCaseLetter      = false ;
  bool hasDecimalDigit         = false ;

  if ( meetsLengthRequirements )
  {
    foreach (char c in password )
    {
      if      ( char.IsUpper(c) ) hasUpperCaseLetter = true ;
      else if ( char.IsLower(c) ) hasLowerCaseLetter = true ;
      else if ( char.IsDigit(c) ) hasDecimalDigit    = true ;
    }
  }

  bool isValid = meetsLengthRequirements
              && hasUpperCaseLetter
              && hasLowerCaseLetter
              && hasDecimalDigit
              ;
  return isValid ;

}

你认为三年后需要修改约束条件的维护程序员更容易理解哪种方式?


我们在后端做类似的事情,但是正则表达式被用于前端的asp.net RegularExpressionValidator上,这样我们就不必每次用户犯错时都要回传。 - desi
2
此客户端JavaScript的复杂程度与上述内容大致相同。 - Nicholas Carey
1
你可以把 if (hasUpperCaseLetter && hasLowerCaseLetter && hasDecimalDigit) return true; 放在 foreach 里面,这样可以避免很多不必要的迭代。 - Justin Morgan
2
@pedram - 为了澄清,我不是在谈论替换foreach的内容。我是说将该行添加为循环的最后一步。这样,只要所有条件都为真,我们就可以立即跳出循环并停止迭代。一旦找到大写字母、小写字母和数字,就没有必要继续检查其他字符。然后在循环之后,您可以简单地放置return false,因为您已经检查了整个字符串,并且没有找到您要查找的所有内容。 - Justin Morgan
1
好的 @JustinMorgan,现在我明白了你的意思。感谢你抽出时间来解释它。 - Pedram
显示剩余3条评论

22

你可以尝试这种方法:

    private bool ValidatePassword(string password, out string ErrorMessage)
        {
            var input = password;
            ErrorMessage = string.Empty;

            if (string.IsNullOrWhiteSpace(input))
            {
                throw new Exception("Password should not be empty");
            }

            var hasNumber = new Regex(@"[0-9]+");
            var hasUpperChar = new Regex(@"[A-Z]+");
            var hasMiniMaxChars = new Regex(@".{8,15}");
            var hasLowerChar = new Regex(@"[a-z]+");
            var hasSymbols = new Regex(@"[!@#$%^&*()_+=\[{\]};:<>|./?,-]");

            if (!hasLowerChar.IsMatch(input))
            {
                ErrorMessage = "Password should contain at least one lower case letter.";
                return false;
            }
            else if (!hasUpperChar.IsMatch(input))
            {
                ErrorMessage = "Password should contain at least one upper case letter.";
                return false;
            }
            else if (!hasMiniMaxChars.IsMatch(input))
            {
                ErrorMessage = "Password should not be lesser than 8 or greater than 15 characters.";
                return false;
            }
            else if (!hasNumber.IsMatch(input))
            {
                ErrorMessage = "Password should contain at least one numeric value.";
                return false;
            }

            else if (!hasSymbols.IsMatch(input))
            {
                ErrorMessage = "Password should contain at least one special case character.";
                return false;
            }
            else
            {
                return true;
            }
        }

2
".{8,15}"仍将匹配一个16个字符的密码。 - Thomas Weller
1
你写了正则表达式hasMiniMaxChars - ".{8,15}" 来检查密码字符长度,但错误信息却提示“密码长度不应小于或大于12个字符”。 - Lost_In_Library
如何仅允许我们正在检查的字符,而不是其他特殊字符或来自其他语言的字母? - Ali123
@Anurag 我怎么在模型里使用这个方法? - Akhtubir
@Akhtubir 这是一种方法,您将密码值传递给该方法并等待结果。 - Anurag

6

对于 Justin 上面的答案进行更新。如果你想在 MVC 中使用数据注释,可以按照以下方式操作:

[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,15}$", ErrorMessage = "Password must be between 6 and 20 characters and contain one uppercase letter, one lowercase letter, one digit and one special character.")]

2

试试这个(还更正了大小写检查,因为你将它们分组为[a-zA-Z],所以只会查找至少一个小写或大写字母。所以要将它们分开):

(?!^[0-9]*$)(?!^[a-z]*$)(?!^[A-Z]*$)^(.{8,15})$

更新:我发现正则表达式并没有像预期的那样起作用,这也不是应该编写的方式!
尝试使用类似以下内容的语法:
(?=^.{8,15}$)(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?!.*\s).*$

(介于8和15之间,包含至少一个数字、至少一个大写字母、至少一个小写字母且没有空格。)

我认为这样更容易理解。


2

我会逐个检查它们;即查找数字\d+,然后如果失败,您可以告诉用户需要添加一个数字。这样避免了返回“无效”错误,没有提示用户出了什么问题。


1
我已经修改了@Anurag在其他问题中的答案以满足我的要求:
需要一个方法返回一个列表,显示密码未符合我的策略的地方,所以首先创建了一个包含所有可能问题的枚举。
public enum PasswordPolicyIssue
{
        MustHaveNumber,
        MustHaveCapitalLetter,
        MustHaveSmallLetter,
        MustBeAtLeast8Characters,
        MustHaveSymbol
}

然后是验证方法,它返回布尔值并具有输出列表:

public bool MeetsPasswordPolicy(string password, out List<PasswordPolicyIssue> issues)
{
        var input = password;
        issues = new List<PasswordPolicyIssue>();

        if (string.IsNullOrWhiteSpace(input))
            throw new Exception("Password should not be empty");

        var hasNumber = new Regex(@"[0-9]+");
        var hasUpperChar = new Regex(@"[A-Z]+");
        //var hasMiniMaxChars = new Regex(@".{8,15}");
        var hasMinChars = new Regex(@".{8,}");
        var hasLowerChar = new Regex(@"[a-z]+");
        var hasSymbols = new Regex(@"[!@#$%^&*()_+=\[{\]};:<>|./?,-]");

        if (!hasLowerChar.IsMatch(input))
            issues.Add(PasswordPolicyIssue.MustHaveSmallLetter);

        if (!hasUpperChar.IsMatch(input))
             issues.Add(PasswordPolicyIssue.MustHaveCapitalLetter);

        if (!hasMinChars.IsMatch(input))
             issues.Add(PasswordPolicyIssue.MustBeAtLeast8Characters);

        if (!hasNumber.IsMatch(input))
             issues.Add(PasswordPolicyIssue.MustHaveNumber);

        if (!hasSymbols.IsMatch(input))
             issues.Add(PasswordPolicyIssue.MustHaveSymbol);
        
        return issues.Count() ==0;
}

@Anurag 的原始回答


简单的在线测试:https://dotnetfiddle.net/cSOFXx - iYazee6

0
^^(?=.*[A-Z]{"+minUpperCase+",})(?=.*[a-z]{"+minLowerCase+",})(?=.*[0-9]{"+minNumerics+",})(?=.*[!@#$\-_?.:{]{"+minSpecialChars+",})[a-zA-Z0-9!@#$\-_?.:{]{"+minLength+","+maxLength+"}$

0

很长,可能可以缩短。支持特殊字符?"-_

\A(?=[-\?\"_a-zA-Z0-9]*?[A-Z])(?=[-\?\"_a-zA-Z0-9]*?[a-z])(?=[-\?\"_a-zA-Z0-9]*?[0-9])[-\?\"_a-zA-Z0-9]{8,15}\z

[\?\"-_a-zA-Z0-9] 中的第一个连字符会导致错误。您需要使用 [-\?\"_a-zA-Z0-9] - Justin Morgan
@Justin:在我发布之前,我已经在RegexBuddy中测试过了,它可以正常工作。你在哪里遇到了错误? - Ken White
1
在某些引擎中,它不一定会抛出错误,但它是无效的正则表达式代码。当您在字符类中的两个字符之间放置连字符且它们之间没有有效的序列时,它会导致未定义的行为。有些人会将其解释为字面连字符,有些人会忽略它,有些人会抛出错误,有些人会使用一些未知的序列方案。无论如何,当您希望将其解释为字面连字符时,它需要被转义或放置在字符类的开头或结尾:[-foo][foo-](或 [^-foo][^foo-])。 - Justin Morgan
1
@Justin:谢谢你提供的信息。这是我之前没有遇到过的情况。显然,我没有使用过字面连字符。我已经相应地更新了我的答案。 - Ken White

0
感谢Nicholas Carey。我本来打算先使用正则表达式,但你写的内容改变了我的想法。这种方式维护起来要容易得多。
//You can set these from your custom service methods
int minLen = 8;
int minDigit 2;
int minSpChar 2;

Boolean ErrorFlag = false;
//Check for password length
if (model.NewPassword.Length < minLen)
{
    ErrorFlag = true;
    ModelState.AddModelError("NewPassword", "Password must be at least " + minLen + " characters long.");
}

//Check for Digits and Special Characters
int digitCount = 0;
int splCharCount = 0;
foreach (char c in model.NewPassword)
{
    if (char.IsDigit(c)) digitCount++;
    if (Regex.IsMatch(c.ToString(), @"[!#$%&'()*+,-.:;<=>?@[\\\]{}^_`|~]")) splCharCount++;
}

if (digitCount < minDigit)
{
    ErrorFlag = true;
    ModelState.AddModelError("NewPassword", "Password must have at least " + minDigit + " digit(s).");
}
if (splCharCount < minSpChar)
{
    ErrorFlag = true;
    ModelState.AddModelError("NewPassword", "Password must have at least " + minSpChar + " special character(s).");
}

if (ErrorFlag)
    return View(model);

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