Java 密码规则验证Passay 库

1. 简介

如今,大多数Web应用程序都有其密码策略 - 简单地说,创建密码策略是为了迫使用户创建难以破解的密码。

要生成此类密码或验证它们,我们可以利用Passay库

2. Maven 依赖

如果我们想在项目中使用 Passay 库,则需要将以下依赖项添加到我们的pom.xml

<dependency>
    <groupId>org.passay</groupId>
    <artifactId>passay</artifactId>
    <version>1.3.1</version>
</dependency>

我们可以在这里找到它。

3. 密码验证

密码验证是Passay库提供的两个主要功能之一。它毫不费力且直观。让我们来发现它。

3.1.密码数据

要验证我们的密码,我们应该使用密码数据。它是验证所需信息的容器。它可以存储以下数据:

  • 密码
  • 用户名
  • 密码引用列表
  • 起源

密码和用户名属性自行解释。Passay 库为我们提供了历史参考和来源参考,我们可以将其添加到密码参考列表中。

我们可以使用 origin 字段来保存有关密码是否由用户生成或定义的信息。

3.2.密码验证器

我们应该知道我们需要PasswordDataPasswordValidator对象来开始验证密码。我们已经讨论了密码数据。现在让我们创建密码验证器

首先,我们应该定义一组密码验证规则。我们必须在创建PasswordValidator对象时将它们传递给构造函数:

PasswordValidator passwordValidator = new PasswordValidator(new LengthRule(5));

有两种方法可以将我们的密码传递给PasswordData对象。我们将其传递给构造函数或 setter 方法:

PasswordData passwordData = new PasswordData("1234");

PasswordData passwordData2 = new PasswordData();
passwordData.setPassword("1234");

我们可以通过在PasswordValidator 上调用validate() 方法来验证我们的密码:

RuleResult validate = passwordValidator.validate(passwordData);

结果,我们将得到一个RuleResult对象。

3.3.规则结果

规则结果包含有关验证过程的有趣信息。它是validate() 方法的结果。

首先,它可以告诉我们密码是否有效:

Assert.assertEquals(false, validate.isValid());

此外,我们可以了解密码无效时返回哪些错误。错误代码和验证说明保存在RuleResultDetail 中:

RuleResultDetail ruleResultDetail = validate.getDetails().get(0);
Assert.assertEquals("TOO_SHORT", ruleResultDetail.getErrorCode());
Assert.assertEquals(5, ruleResultDetail.getParameters().get("minimumLength"));
Assert.assertEquals(5, ruleResultDetail.getParameters().get("maximumLength"));

最后,我们可以使用RuleResultMetadata探索密码验证的元数据:

Integer lengthCount = validate
  .getMetadata()
  .getCounts()
  .get(RuleResultMetadata.CountCategory.Length);
Assert.assertEquals(Integer.valueOf(4), lengthCount);

4. 密码生成

除了验证之外,Passay库还使我们能够生成密码。我们可以提供生成器应该使用的规则。

要生成密码,我们需要有一个PasswordGenerator对象。一旦有了它,我们调用generatePassword()方法并传递CharacterRules列表。下面是一个示例代码:

CharacterRule digits = new CharacterRule(EnglishCharacterData.Digit);

PasswordGenerator passwordGenerator = new PasswordGenerator();
String password = passwordGenerator.generatePassword(10, digits);

Assert.assertTrue(password.length() == 10);
Assert.assertTrue(containsOnlyCharactersFromSet(password, "0123456789"));

我们应该知道我们需要一个CharacterData对象来创建CharacterRule另一个有趣的事实是,该库为我们提供了英语字符数据。它是五组字符的枚举:

  • 数字
  • 小写英文字母
  • 大写英文字母
  • 小写和大写集合的组合
  • 特殊字符

但是,没有什么可以阻止我们定义我们的字符集。这就像实现CharacterData接口一样简单。让我们看看我们如何做到这一点:

CharacterRule specialCharacterRule = new CharacterRule(new CharacterData() {
    @Override
    public String getErrorCode() {
        return "SAMPLE_ERROR_CODE";
    }

    @Override
    public String getCharacters() {
        return "ABCxyz123!@#";
    }
});

PasswordGenerator passwordGenerator = new PasswordGenerator();
String password = passwordGenerator.generatePassword(10, specialCharacterRule);

Assert.assertTrue(containsOnlyCharactersFromSet(password, "ABCxyz123!@#"));

5. 正匹配规则

我们已经学习了如何生成和验证密码。为此,我们需要定义一组规则。因此,我们应该知道Passay 中有两种类型的规则:正匹配规则和负匹配规则。

首先,让我们找出什么是积极的规则以及如何使用它们。

正匹配规则接受包含提供的字符、正则表达式或符合某些限制的密码。

有六个正匹配规则:

  • 允许字符规则 – 定义密码必须包含的所有字符
  • AllowedRegexRule–定义密码必须匹配的正则表达式
  • 字符规则–定义密码中应包含的字符集和最少数量的字符
  • 长度规则 – 定义密码的最小长度
  • 字符特征规则 – 检查密码是否满足定义的规则的N个。
  • 长度复杂性规则 – 允许我们为不同的密码长度定义不同的规则

5.1. 简单正匹配规则

现在,我们将介绍具有简单配置的所有规则。它们定义了一组合法字符或模式或可接受的密码长度。

以下是所讨论规则的简短示例:

PasswordValidator passwordValidator = new PasswordValidator(
  new AllowedCharacterRule(new char[] { 'a', 'b', 'c' }), 
  new CharacterRule(EnglishCharacterData.LowerCase, 5), 
  new LengthRule(8, 10)
);

RuleResult validate = passwordValidator.validate(new PasswordData("12abc"));

assertFalse(validate.isValid());
assertEquals(
  "ALLOWED_CHAR:{illegalCharacter=1, matchBehavior=contains}", 
  getDetail(validate, 0));
assertEquals(
  "ALLOWED_CHAR:{illegalCharacter=2, matchBehavior=contains}", 
  getDetail(validate, 1));
assertEquals(
  "TOO_SHORT:{minimumLength=8, maximumLength=10}", 
  getDetail(validate, 4));

我们可以看到,如果密码无效,每个规则都给了我们明确的解释。有通知指出密码太短并且有两个非法字符。我们还可以注意到密码与提供的正则表达式不匹配。

更重要的是,我们被告知它包含的小写字母不足。

5.2.字符特征规则

CharcterCharacterisitcsRule比之前提出的规则更复杂。要创建一个CharcterCharacterisitcsRule对象,我们需要提供一个CharacterRule 的列表更重要的是,我们必须设置密码必须匹配多少个。我们可以这样做:

CharacterCharacteristicsRule characterCharacteristicsRule = new CharacterCharacteristicsRule(
  3, 
  new CharacterRule(EnglishCharacterData.LowerCase, 5), 
  new CharacterRule(EnglishCharacterData.UpperCase, 5), 
  new CharacterRule(EnglishCharacterData.Digit),
  new CharacterRule(EnglishCharacterData.Special)
);

呈现的字符特征规则要求密码包含提供的四个规则中的三个。

5.3.长度复杂性规则

另一方面,Passay库为我们提供了LengthComplexityRule它允许我们定义哪些规则应应用于哪个长度的密码。CharacterFeaturesRule相比,它们允许我们使用所有类型的规则 - 不仅仅是CharacterRule

我们来分析一下这个例子:

LengthComplexityRule lengthComplexityRule = new LengthComplexityRule();
lengthComplexityRule.addRules("[1,5]", new CharacterRule(EnglishCharacterData.LowerCase, 5));
lengthComplexityRule.addRules("[6,10]", 
  new AllowedCharacterRule(new char[] { 'a', 'b', 'c', 'd' }));

正如我们所看到的,对于具有一到五个字符的密码,我们应用字符规则。但是对于包含六到十个字符的密码,我们希望密码与AllowedCharacterRule 匹配。

6. 负匹配规则

与正匹配规则不同,负匹配规则拒绝包含提供的字符、正则表达式、条目等的密码。

让我们找出负匹配规则是什么:

  • 非法字符规则 – 定义密码不得包含的所有字符
  • 非法正则表达式规则 – 定义一个不能匹配的正则表达式
  • IllegalSequenceRule– 检查密码是否具有非法的字符序列
  • NumberRangeRule–定义密码不得包含的数字范围
  • 空白规则–检查密码是否包含空格
  • 字典规则 – 检查密码是否等于任何字典记录
  • 字典子字符串规则 – 检查密码是否包含任何字典记录
  • 历史规则 – 检查密码是否包含任何历史密码引用
  • 摘要历史记录规则 – 检查密码是否包含任何摘要历史密码引用
  • 源规则 – 检查密码是否包含任何源密码引用
  • 摘要源规则 – 检查密码是否包含任何摘要源密码引用
  • 用户名规则 – 检查密码是否包含用户名
  • RepeatCharacterRegexRule– 检查密码是否包含重复的ASCII字符

6.1. 简单负匹配规则

首先,我们将看到如何使用简单的规则,例如IllegalCharacterRuleIllegalRegexRule等。下面是一个简短的示例:

PasswordValidator passwordValidator = new PasswordValidator(
  new IllegalCharacterRule(new char[] { 'a' }), 
  new NumberRangeRule(1, 10), 
  new WhitespaceRule()
);

RuleResult validate = passwordValidator.validate(new PasswordData("abcd22 "));

assertFalse(validate.isValid());
assertEquals(
  "ILLEGAL_CHAR:{illegalCharacter=a, matchBehavior=contains}", 
  getDetail(validate, 0));
assertEquals(
  "ILLEGAL_NUMBER_RANGE:{number=2, matchBehavior=contains}", 
  getDetail(validate, 4));
assertEquals(
  "ILLEGAL_WHITESPACE:{whitespaceCharacter= , matchBehavior=contains}", 
  getDetail(validate, 5));

该示例向我们展示了所描述的规则是如何工作的。与正匹配规则类似,它们为我们提供了有关验证的完整反馈。

6.2. 字典规则

如果我们想检查密码是否不等于提供的单词怎么办。

出于这个原因,Passay库为我们提供了出色的工具。让我们发现DictionaryRuleDictionarySubstringRule

WordListDictionary wordListDictionary = new WordListDictionary(
  new ArrayWordList(new String[] { "bar", "foobar" }));

DictionaryRule dictionaryRule = new DictionaryRule(wordListDictionary);
DictionarySubstringRule dictionarySubstringRule = new DictionarySubstringRule(wordListDictionary);

我们可以看到字典规则使我们能够提供禁用单词的列表。当我们有一个最常见或最容易破解的密码列表时,这是有益的。因此,禁止用户使用它们是合理的。

在现实生活中,我们肯定会从文本文件或数据库中加载单词列表。在这种情况下,我们可以使用单词列表。它有三个重载的方法,这些方法采用一个读取器数组并创建ArrayWordList

6.3.历史规则和源规则

此外,Passay库为我们提供了HistoryRuleSourceRule。他们可以根据历史密码或来自各种来源的文本内容验证密码。

让我们看一下这个例子:

SourceRule sourceRule = new SourceRule();
HistoryRule historyRule = new HistoryRule();

PasswordData passwordData = new PasswordData("123");
passwordData.setPasswordReferences(
  new PasswordData.SourceReference("source", "password"), 
  new PasswordData.HistoricalReference("12345")
);

PasswordValidator passwordValidator = new PasswordValidator(
  historyRule, sourceRule);

历史记录规则可帮助我们检查以前是否使用过密码。由于这种做法不安全,因此我们不希望用户使用旧密码。

另一方面,SourceRule允许我们检查密码是否与SourceReferences中提供的密码不同。我们可以避免在不同系统或应用程序中使用相同的密码的风险。

值得一提的是,有DigestSourceRuleDigestHistoryRule这样的规则。我们将在下一段中介绍它们。

6.4. 摘要规则

Passay库中有两个摘要规则:DigestHistoryRule 和 DigestSourceRule。摘要规则旨在处理存储为摘要或哈希的密码。因此,要定义它们,我们需要提供一个EncodingHashBean对象。

让我们看看它是如何完成的:

List<PasswordData.Reference> historicalReferences = Arrays.asList(
  new PasswordData.HistoricalReference(
    "SHA256",
    "2e4551de804e27aacf20f9df5be3e8cd384ed64488b21ab079fb58e8c90068ab"
));

EncodingHashBean encodingHashBean = new EncodingHashBean(
  new CodecSpec("Base64"), 
  new DigestSpec("SHA256"), 
  1, 
  false
);

这次我们通过标签和构造函数的编码密码创建历史引用。之后,我们使用适当的编解码器和摘要算法实例化了EncodingHashBean

此外,我们可以指定迭代次数以及算法是否加盐。

一旦我们有一个编码 bean,我们就可以验证我们的摘要密码:

PasswordData passwordData = new PasswordData("example!");
passwordData.setPasswordReferences(historicalReferences);

PasswordValidator passwordValidator = new PasswordValidator(new DigestHistoryRule(encodingHashBean));

RuleResult validate = passwordValidator.validate(passwordData);

Assert.assertTrue(validate.isValid());

我们可以在Cryptacular library网页上了解有关EncodingHashinBean的更多信息。

6.5.重复字符正则表达式规则

另一个有趣的验证规则是RepeatCharacterRegexRule我们可以使用它来检查密码是否包含重复的ASCII字符。

下面是一个示例代码:

PasswordValidator passwordValidator = new PasswordValidator(new RepeatCharacterRegexRule(3));

RuleResult validate = passwordValidator.validate(new PasswordData("aaabbb"));

assertFalse(validate.isValid());
assertEquals("ILLEGAL_MATCH:{match=aaa, pattern=([^\\x00-\\x1F])\\1{2}}", getDetail(validate, 0));

6.6.用户名规则

我们将在本章中讨论的最后一个规则是用户名规则它使我们能够禁止在密码中使用用户名。

正如我们之前所了解的,我们应该将用户名存储在PasswordData 中:

PasswordValidator passwordValidator = new PasswordValidator(new UsernameRule());

PasswordData passwordData = new PasswordData("testuser1234");
passwordData.setUsername("testuser");

RuleResult validate = passwordValidator.validate(passwordData);

assertFalse(validate.isValid());
assertEquals("ILLEGAL_USERNAME:{username=testuser, matchBehavior=contains}", getDetail(validate, 0));

7. 自定义消息

Passay库使我们能够自定义验证规则返回的消息。首先,我们应该定义消息并将它们分配给错误代码。

我们可以将它们放入一个简单的文件中。让我们看看它有多容易:

TOO_LONG=Password must not have more characters than %2$s.
TOO_SHORT=Password must not contain less characters than %2$s.

获得消息后,我们必须加载该文件。最后,我们可以将其传递给PasswordValidator对象。

下面是一个示例代码:

URL resource = this.getClass().getClassLoader().getResource("messages.properties");
Properties props = new Properties();
props.load(new FileInputStream(resource.getPath()));

MessageResolver resolver = new PropertiesMessageResolver(props);

如我们所见,我们已经加载了message.properties文件并将其传递到Properties对象中。然后,我们可以使用Properties对象来创建PropertiesMessageResolver

让我们看一下如何使用消息解析器的示例:

PasswordValidator validator = new PasswordValidator(
  resolver, 
  new LengthRule(8, 16), 
  new WhitespaceRule()
);

RuleResult tooShort = validator.validate(new PasswordData("XXXX"));
RuleResult tooLong = validator.validate(new PasswordData("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"));

Assert.assertEquals(
  "Password must not contain less characters than 16.", 
  validator.getMessages(tooShort).get(0));
Assert.assertEquals(
  "Password must not have more characters than 16.", 
  validator.getMessages(tooLong).get(0));

该示例清楚地表明,我们可以使用配备消息解析器的验证器来翻译所有错误代码。

8. 结论

在本教程中,我们学习了如何使用Passay库。我们分析了几个示例,说明如何轻松使用该库进行密码验证。提供的规则涵盖了确保密码安全的大多数常用方法。

但是我们应该记住,Passay 库本身并不能使我们的密码安全。首先,我们应该学习什么是一般规则,然后使用库来实现它们。

与往常一样,所有示例都可以在GitHub上找到。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值