65.9K
CodeProject 正在变化。 阅读更多。
Home

Xander.PasswordValidator 介绍

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (8投票s)

2013年4月10日

CPOL

11分钟阅读

viewsIcon

26922

downloadIcon

429

Xander.PasswordValidator 是一个(或两个)程序集,用于帮助在 .NET 应用程序中验证密码。

引言

Password Validator 是一个(或两个)程序集,用于帮助在 .NET 应用程序中验证密码。

主程序集执行实际的验证,还有一个用于 Web 应用程序的程序集,可以更轻松地将验证添加到 ASP.NET MVC 应用程序中。

背景

我正在为即将进行的项目寻找一种验证密码的方法,因为我们需要比以往更好地做到这一点,所以我一直在寻找一些 .NET 库来完成这项工作。不幸的是,Google 只找到了大量的正则表达式,这都没什么问题,但这并不是我真正想要的。我真的希望能够与潜在密码的字典进行比较,并因为它存在于列表中而拒绝密码,同时它还必须检查“必须包含数字”等常规规则。

于是,我创建了 Xander.PasswordValidator。

这个想法是,您可以创建一个 Validator 对象,然后可以使用它来评估潜在密码的适用性。在创建验证器时,您可以从代码传递设置,或者让它从配置文件中读取设置。您可以扩展其功能,所以如果您有我想到的内容但我没有想到的,您就有可以扩展的点。如果禁止密码列表不合适,您可以添加自己的列表;如果针对这些列表的检查规则不合适,您可以添加自己的检查方法。

此外,如果您使用 ASP.NET MVC 编写 Web 应用程序,那么您可以在模型中的属性上应用一个非常简单的属性,以让验证器检查该值。

入门

程序集已在 NuGet 上发布,因此非常容易上手。但是,如果您想自己构建程序集,那么源代码在 GitHub 上提供,并采用 MIT 许可。

使用基本验证器

其核心是 Validator 类。它执行密码验证,并向调用者返回一个布尔值,告知他们验证是成功还是失败。

Validator 可以接受调用者设置的设置,也可以在应用程序的配置文件中查找设置。

这是一个简单的示例

var settings = new PasswordValidationSettings();
settings.MinimumPasswordLength = 6;
settings.NeedsLetter = true;
settings.NeedsNumber = true;
settings.StandardWordLists.Add(StandardWordList.MostCommon500Passwords);
var validator = new Validator(settings);
bool result = validator.Validate("MySuperSecretPassword");

首先,创建一个设置类,然后设置各种选项。如果您不设置任何选项,则验证器允许任何密码。

在此示例中,设置要求密码至少有六个字符(MinimumPasswordLength),必须包含一个字母(NeedsLetter),必须包含一个数字(NeedsNumber),并且不得出现在内置的最常见 500 个密码列表中(StarndardWordLists.Add(StandardWordList.MostCommon500Passwords)。

然后创建 Validator 并将其传递给您已准备好的设置。

最后,调用 Validate 方法并传入要验证的密码。结果将指示密码是否通过或失败(在上面的示例中,它失败了,因为它不包含数字)。

配置文件中的设置

如果您更喜欢在配置文件中设置验证器的设置,那么您可以实例化一个 Validator,而无需向其构造函数传递任何内容,它将改用配置文件中的设置。

不言而喻,您应该只在安全的坏境中将设置放在配置文件中。而且,无论如何,某些配置元素根本无法在配置文件中使用,例如指定您自己的验证检查例程的能力。

要使用配置文件中的设置,您必须设置一个用于存放设置的节,然后创建包含这些设置的节。

定义节

<configSections>
<!-- Set up other config sections here—>
   <sectionGroup name="passwordValidation">
      <section name="rules" 
        type="Xander.PasswordValidator.Config.PasswordValidationSection, Xander.PasswordValidator, 
          Version=1.0.0.0, Culture=neutral, PublicKeyToken=fe72000dffcf195f" 
        allowLocation="true" allowDefinition="Everywhere"/>
   </sectionGroup>
</configSections>

以及配置节本身的示例

<!-- The configuration section that describes the configuration for the password validation -->
<passwordValidation>
   <rules minimumPasswordLength="6" needsNumber="false" 
               needsLetter="false" needsSymbol="false">
     <wordListProcessOptions checkForNumberSuffix="true" 
            checkForDoubledUpWord="true" checkForReversedWord="true" />
     <standardWordLists>
       <add value="FemaleNames"/>
       <add value="MaleNames"/>
       <add value="MostCommon500Passwords"/>
       <add value="Surnames"/>
     </standardWordLists>
     <customWordLists>
       <add file="WordLists/MyCustomWordList.txt" />
       <add file="WordLists/MyOtherCustomWordList.txt" />
     </customWordLists>
   </rules>
</passwordValidation>

规则元素

规则部分定义了密码将如何被验证的实际规则。

<rules minimumPasswordLength="13" needsNumber="true" needsLetter="true" needsSymbol="true">
  • minimumPasswordLength:一个正整数,定义了有效密码所需的最小字符数。它是可选的,如果缺失则默认为 8
  • needsNumber:一个布尔值,指示密码是否需要包含数字。它是可选的,如果缺失则默认为 true
  • needsLetter:一个布尔值,指示密码是否需要包含字母。它是可选的,如果缺失则默认为 true
  • needsSymbol:一个布尔值,指示密码是否需要包含符号。它是可选的,如果缺失则默认为 false

规则可以有多个子元素。

  • wordListProcessOptions:一组用于处理单词列表的选项。
  • standardWordLists:要用于与密码进行比较的内置单词列表集合。
  • customWordLists:要用于与密码进行比较的自定义单词列表集合。

单词列表处理选项

默认情况下,将密码与单词列表进行比较只会检查密码是否在单词列表中。这些是与单词列表进行比较的附加选项。

<wordListProcessOptions checkForNumberSuffix="true" 
          checkForDoubledUpWord="true" checkForReversedWord="true" />
  • checkForNumberSuffix:指示是否应检查密码是否只是单词列表后附加了数字。这是可选的,默认值为 false
  • checkForDoubledUpWord:指示是否应检查密码是否是相同的序列重复出现,如果是,则检查第一个一半是否在单词列表中。这是可选的,默认值为 false
  • checkForReversedWord:指示是否应检查密码的反向形式是否在单词列表中。这是可选的,默认值为 false。

标准单词列表

此元素是标准单词列表项集合的容器。

<standardwordlists>
   <add value="FemaleNames" />
   <add value="MaleNames" />
   <add value="MostCommon500Passwords" />
   <add value="Surnames" />
</standardwordlists>

有效的单词列表是

自定义单词列表

此元素是包含自定义单词列表的纯文本文件路径的容器。单词列表文件只是一个纯文本文件,每行一个单词。

<customWordLists>
   <add file="WordLists/MyCustomWordList.txt" />
   <add file="WordLists/MyOtherCustomWordList.txt" />
</customWordLists>

路径相对于密码验证器运行的应用程序的工作目录。在 ASP.NET Web 应用程序中,路径应以 ~ 开头,以确保它们在服务器上相对于 Web 应用程序的根目录正确映射。(请参阅下一节关于在 Web 应用程序中使用 Xander.PasswordValidator 的内容。)

在 Web 应用程序中使用 Xander.PasswordValidator

如上所述,有两个 NuGet 包。在 MVC 应用程序中使用 Xander.PasswordValidator 需要使用第二个包,即 Xander.PasswordValidator.Web NuGet 包,如果您想在模型属性中使用属性装饰。此包依赖于第一个包,因此您只需告诉 NuGet 在应用程序中安装此包,它就会自动获取第一个包。

最简单的方法就是用 PasswordValidationAttribute 来装饰模型中的属性,如下所示

public class SomeModel
{
 [PasswordValidation]
 public string Password { get; set; }

  // Other stuff goes here
}

这将根据配置文件中的设置来验证密码。

如果您想使用自定义单词列表,还需要执行一个额外的步骤。

注册密码验证器

为了使自定义单词列表的文件路径在 Web 应用程序中正确解析,您需要在 Web 应用程序的 HttpApplication 派生类中的 Application_Start() 方法中(或在首次使用之前)注册验证器。

例如,Application_Start() 方法可能如下所示

protected void Application_Start()
{
   PasswordValidatorRegistration.Register(); // Register password validator
   AreaRegistration.RegisterAllAreas();
   RegisterGlobalFilters(GlobalFilters.Filters);
   RegisterRoutes(RouteTable.Routes);
}

在 Web 应用程序中使用代码中的设置进行验证

由于设置可能变得相当复杂,因此无法直接在用于装饰模型的属性中进行设置。相反,它们可以在其他地方设置,并在属性中引用。

设置可以按常规方式配置,然后添加到 PasswordValidationSettingsCache。例如

var settings = new PasswordValidationSettings();
settings.NeedsNumber = true;
settings.NeedsSymbol = true;
settings.MinimumPasswordLength = 6;
settings.StandardWordLists.Add(StandardWordList.FemaleNames);
settings.StandardWordLists.Add(StandardWordList.MaleNames);
settings.StandardWordLists.Add(StandardWordList.Surnames);
settings.StandardWordLists.Add(StandardWordList.MostCommon500Passwords);
PasswordValidationSettingsCache.Add("StandardRules", settings);

此代码通常应放在 Application_Start() 方法中,或由其调用,在注册密码验证器之后。

重要的一行是最后一行。它将设置添加到缓存中,名称为 StandardRules,之后可以在属性中引用。如下所示

public class MyModel
{
  [PasswordValidation("StandardRules")]
  public string Password { get; set; } 
}

PasswordValidationAttribute 引用缓存中的条目,然后检索该条目以执行验证。

您还可以将标准的 ErrorMessage 参数包含到属性中,以便显示的错误消息根据您的应用程序进行自定义。例如

public class MyModel
{
  [PasswordValidation("StandardRules", 
    ErrorMessage="Passwords must be at least 6 characters and contains numbers and symbols.")]
  public string Password { get; set; } 
}

提供您自己的验证处理程序

如果密码验证器没有您项目所需的验证规则,那么它是易于扩展的。您可以创建自己的 ValidationHander 派生类,并通过传递给 ValidatorPasswordValdiationSettings 对象来添加它们。

ValidationHandler

ValidationHandler 类是一个抽象基类,它在 Xander.PasswordValidator 框架本身中被扩展,以提供各种内置的验证例程。(您可以在可下载的演示项目以及 GitHub 上的源代码中看到其中一些示例。)

您可以通过创建一个类并设置 Xander.PasswordValidator.ValidationHandler 作为基类并重写 Validate() 方法来创建自己的验证器。

Validate 方法只接受密码作为参数并返回一个布尔值,返回 true 表示密码通过验证,返回 false 表示密码失败验证。

例如,这里有一个非常简单的验证器,它会拒绝看起来像日期的密码

using System;
using Xander.PasswordValidator;

namespace Demo.ValidationHandlers
{
  public class NoDatesValidationHandler : ValidationHandler
  {
    public override bool Validate(string password)
    {
      DateTime date;
      var parseResult = DateTime.TryParse(password, out date);
      return !parseResult;
    }
  }
}

要设置此验证器使其被调用,需要将其作为设置的一部分添加。您需要传递处理程序的类型。如果需要,验证框架将为您创建一个处理程序实例。如果验证在运行您的验证器之前失败,则您的验证器不会运行。

var settings = new PasswordValidationSettings();
settings.CustomValidators.Add(typeof(NoDatesValidationHandler));

CustomValidationHandler<TData>

它派生自 ValidationHandler,当您需要将一些附加数据或对象传递给您的验证处理程序以使其正常工作时使用。

Validate 方法的工作方式与以前相同,除了现在您还可以从基类中访问一个包含您的自定义数据的附加属性,称为 CustomDataCustomData 是通过设置传递的对象。

要通过设置传递数据,请使用 PasswordValidationSettings 对象上的 CustomSettings 属性。例如

settings = new PasswordValidationSettings();
settings.MinimumPasswordLength = 6;
settings.CustomValidators.Add(typeof(PasswordHistoryValidationHandler));
settings.CustomSettings.Add(typeof(PasswordHistoryValidationHandler), new Repository());

传递给 CustomSettings 的键是引用要发送设置的 ValidationHandler 类型的类型。

自定义 ValidationHandler 看起来像这样

using System.Linq;
using System.Web;
using Xander.PasswordValidator;

namespace Demo.ValidationHandlers
{
  public class PasswordHistoryValidationHandler : CustomValidationHandler<Repository>
  {
    public override bool Validate(string password)
    {
      var user = HttpContext.Current.User;
      var history = CustomData.GetPasswordHistory(user.Identity.Name);
      return !history.Any(h => string.Compare(password, h, true) == 0);
    }
  }
}

在上面的示例中,设置传递了一个存储库,该存储库在运行 Validator 时传递给 ValidationHandler。存储库用于获取密码的历史记录(在此示例中是一个虚拟存储库——在实际生活中,您永远不应该像这样访问明文密码),并且可以检查历史记录与当前密码,以确保它不匹配任何历史密码。

自定义单词列表处理

处理单词列表时,会构建一个正则表达式,以便快速遍历列表。正则表达式是使用多个构建器构建的,这些构建器创建正则表达式的各个部分。一个用于检查密码本身,另一个用于测试密码与列表的比较,但通过添加数字后缀进行修改。您可以创建自己的派生自 WordListRegExBuilder 的类,然后将其添加到 WordListProcessOptions 中。

WordListRegExBuilder 是一个抽象基类,要求在任何具体的派生类中实现 GetRegularExpression 方法。它还有一个名为 RegExEncode 的方法,该方法接受一个字符串并对其进行编码以在正则表达式中使用,转义所有正则表达式引擎使用的特殊符号。

例如,假设您想根据列表验证密码,但还要检查数字前缀,您可以创建一个类来构建正则表达式的这一部分。该类看起来像这样

using Xander.PasswordValidator;
namespace Xander.Demo.PasswordValidator.Web.Mvc3.Helpers
{
  public class NumericPrefixBuilder : WordListRegExBuilder
  {
    public override string GetRegularExpression(string password)
    {
      return "[0-9]" + RegExEncode(password);
    }
  }
}

要在验证器中使用它,请将其添加到设置中,如下所示

var settings = new PasswordValidationSettings();
settings.WordListProcessOptions.CustomBuilders.Add(typeof(NumericPrefixBuilder));

Validator 将创建您类的实例并运行 GetRegularExpression 方法。然后,它将该方法合并到它正在构建的正则表达式中,并使用它来测试单词列表。

演示

与本文一起提供的是两个密码验证器演示。

第一个是一个相当简单的控制台应用程序,它从键盘接收输入,并告诉用户验证是成功还是失败。这可以轻松修改,以便您可以自己尝试各种设置。

第二个是一个 MVC 4 应用程序,演示了在 Web 上下文中工作的验证器。

更多资源

历史

  • 2013 年 4 月 10 日:初始版本。
© . All rights reserved.