Guide to Passay – 帕萨伊指南

最后修改: 2018年 9月 30日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Nowadays, most web applications have their password policy – which is, simply put, created to force users to create difficult to break passwords.

现在,大多数网络应用都有自己的密码策略–简单地说,就是为了迫使用户创建难以破解的密码而设立的。

To generate such passwords or validate them we can make use of Passay library.

为了生成这种密码或验证它们,我们可以利用Passay库

2. Maven Dependency

2.Maven的依赖性

If we want to use Passay library in our project, it’s necessary to add the following dependency to our pom.xml:

如果我们想在我们的项目中使用Passay库,有必要在我们的pom.xml中添加以下依赖。

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

We can find it here.

我们可以在这里找到它。

3. Password Validation

3.密码验证

Password validation is one of two main functionalities provided by Passay library. It’s effortless and intuitive. Let’s discover it.

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

3.1. PasswordData

3.1.密码数据(PasswordData)

To validate our password, we should use PasswordData. It’s a container for information that is necessary for validation. It can store such data as:

为了验证我们的密码,我们应该使用PasswordData。它是验证所需信息的容器。它可以存储这样的数据。

  • password
  • username
  • list of password references
  • origin

Password and username properties explain themselves. Passay library gives us HistoricalReference and SourceReference which we can add to the list of password references.

密码和用户名属性自己解释。Passay库给了我们HistoricalReferenceSourceReference,我们可以把它们添加到密码引用的列表中。

We can use the origin field to hold information about whether the password was generated or defined by a user.

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

3.2. PasswordValidator

3.2.PasswordValidator

We should know that we need PasswordData and PasswordValidator objects to start validating passwords. We’ve already discussed PasswordData. Let’s create PasswordValidator now.

我们应该知道,我们需要PasswordDataPasswordValidator对象来开始验证密码。我们已经讨论过PasswordData。现在让我们来创建PasswordValidator

Firstly, we should define a set of rules for password validation. We have to pass them to constructor while creating a PasswordValidator object:

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

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

There are two ways of passing our password to a PasswordData object. We pass it to either the constructor or the setter method:

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

PasswordData passwordData = new PasswordData("1234");

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

We can validate our password by calling validate() method on PasswordValidator:

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

RuleResult validate = passwordValidator.validate(passwordData);

As a result, we’ll get a RuleResult object.

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

3.3. RuleResult

3.3.规则结果

RuleResult holds interesting information about a validation process. It comes as a result of the validate() method.

RuleResult持有关于一个验证过程的有趣信息。它是validate()方法的一个结果。

First of all, it can tell us whether the password is valid:

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

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

Moreover, we can learn what errors are returned when the password is invalid. Error codes and validation descriptions are kept in RuleResultDetail:

此外,我们可以了解当密码无效时,会返回哪些错误。错误代码和验证描述被保存在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"));

Finally, we can explore the password validation’s metadata with RuleResultMetadata:

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

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

4. Password Generation

4.密码生成

In addition to validation, the Passay library enables us to generate passwords. We can provide rules which the generator should use.

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

To generate a password, we need to have a PasswordGenerator object. Once we have it, we call the generatePassword() method and pass list of CharacterRules. Here is a sample code:

为了生成一个密码,我们需要有一个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"));

We should know that we need an object of CharacterData to create CharacterRule. Another interesting fact is that the library provides us with EnglishCharacterData. It is an enum of five sets of characters:

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

  • digits
  • lowercase English alphabet
  • uppercase English alphabet
  • combination of lowercase and uppercase sets
  • special characters

However, nothing can stop us from defining our set of characters. It’s as straightforward as implementing the CharacterData interface. Let’s see how we can do it:

然而,没有什么能阻止我们定义我们的字符集。这就像实现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. Positive Matching Rules

5.正面的匹配规则

We’ve already learned how we can generate and validate passwords. To do that, we need to define a set of rules. For that reason, we should know that there are two types of rules available in Passay: positive matching rules and negative matching rules.

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

Firstly, let’s find out what are the positive rules and how we can use them.

首先,让我们了解一下什么是积极的规则,以及我们如何使用它们。

Positive matching rules accept passwords which contain provided characters, regular expressions or fits in some limitations.

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

There are six positive matching rules:

有六个积极的匹配规则。

  • AllowedCharacterRule – defines all characters that the password must include
  • AllowedRegexRule – defines a regular expression which the password must match
  • CharacterRule – defines a character set and a minimal number of characters that should be included in the password
  • LengthRule – defines a minimal length of the password
  • CharacterCharacteristicsRule – checks whether the password fulfills N of defined rules.
  • LengthComplexityRule – allows us to define different rules for different password lengths

5.1. Simple Positive Matching Rules

5.1.简单的正向匹配规则

Now, we’ll cover all the rules that have a simple configuration. They define a set of legal characters or patterns or an acceptable password’s length.

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

Here’s a short example of the discussed rules:

下面是一个讨论过的规则的简短例子。

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));

We can see that each rule gives us a clear explanation if the password is not valid. There are notifications that the password is too short and has two illegal characters. We can also notice that the password doesn’t match the provided regular expression.

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

What’s more, we’re informed that it contains insufficient lowercase letters.

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

5.2. CharacterCharacterisitcsRule

5.2.CharacterCharacterisitcsRule

CharcterCharacterisitcsRule is more complex than rules presented before. To create a CharcterCharacterisitcsRule object, we need to provide a list of CharacterRules. What’s more, we have to set how many of them the password must match. We can do it this way:

CharcterCharacterisitcsRule比之前介绍的规则更复杂。为了创建一个CharcterCharacterisitcsRule对象,我们需要提供一个CharacterRules的列表。更重要的是,我们必须设置密码必须匹配其中的多少条。我们可以这样做。

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

Presented CharacterCharacteristicsRule requires a password to contain three of four provided rules.

提出的CharacterCharacteristicsRule要求密码包含所提供的四个规则中的三个。

5.3. LengthComplexityRule

5.3.LengthComplexityRule

On the other hand, Passay library provides us with LengthComplexityRule. It allows us to define which rules should be applied to the password of which length. In contrast to CharacterCharacteristicsRule, they allow us to use all kind of rules – not only CharacterRule.

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

Let’s analyze the example:

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

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' }));

As we can see for password having one to five characters, we apply CharacterRule. But for a password containing six to ten characters, we want the password to match AllowedCharacterRule.

我们可以看到,对于有1到5个字符的密码,我们应用CharacterRule。但对于包含六到十个字符的密码,我们希望密码符合AllowedCharacterRule

6. Negative Matching Rules

6.负数匹配规则

Unlike positive matching rules, negative matching rules reject passwords that contain provided characters, regular expressions, entries, etc.

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

Let’s find out what are the negative matching rules:

让我们来看看什么是负面匹配规则。

  • IllegalCharacterRule – defines all characters that a password mustn’t contain
  • IllegalRegexRule – defines a regular expression which mustn’t match
  • IllegalSequenceRule – checks whether a password has an illegal sequence of characters
  • NumberRangeRule – defines a range of numbers which a password mustn’t contain
  • WhitespaceRule – checks whether a password contains whitespaces
  • DictionaryRule – checks whether a password is equal to any dictionary record
  • DictionarySubstringRule – checks whether a password contain any dictionary record
  • HistoryRule – checks whether a password contains any historical password reference
  • DigestHistoryRule – checks whether a password contains any digested historical password reference
  • SourceRule – checks whether a password contains any source password reference
  • DigestSourceRule – checks whether a password contains any digest source password reference
  • UsernameRule – checks whether a password contains a username
  • RepeatCharacterRegexRule – checks whether a password contains repeated ASCII characters

6.1. Simple Negative Matching Rules

6.1.简单的否定式匹配规则

Firstly, we’re going to see how we can use simple rules such as IllegalCharacterRule, IllegalRegexRule, etc. Here is a short example:

首先,我们要看看如何使用简单的规则,如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));

The example shows us how the described rules work. Similarly to positive matching rules, they give us full feedback about validation.

这个例子向我们展示了描述的规则是如何工作的。与正向匹配规则类似,它们给了我们关于验证的全面反馈。

6.2. Dictionary Rules

6.2.词典规则

What if we want to check whether a password is not equal to provided words.

如果我们想检查一个密码是否与所提供的单词不一样,该怎么办?

For that reason, the Passay library gives us excellent tools for that. Let’s discover DictionaryRule and DictionarySubstringRule:

出于这个原因,Passay库给了我们很好的工具。让我们发现DictionaryRuleDictionarySubstringRule

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

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

We can see dictionary rules enable us to provide a list of banned words. It’s beneficial when we have a list of the most common or the easiest to break passwords. Therefore, it’s reasonable to prohibit users from using them.

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

In real life, we would certainly load a list of words from a text file or a database. In that case, we can use WordLists. It has three overloaded methods that take an array of Readers and create ArrayWordList.

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

6.3. HistoryRule and SourceRule

6.3.HistoryRuleSourceRule

Furthermore, the Passay library gives us HistoryRule and SourceRule. They can validate passwords against historical passwords or text content from various sources.

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

Let’s take a look at the example:

让我们看一下这个例子。

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);

HistoryRules help us checking whether a password has been used before. Because such practices are insecure, we don’t want users to use old passwords.

HistoryRules帮助我们检查一个密码是否曾经被使用过。因为这种做法是不安全的,我们不希望用户使用旧密码。

On the other hand, SourceRule allows us to check whether the password is different than those provided in SourceReferences. We can avoid the risk of having the same passwords in different systems or applications.

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

It’s worth mentioning that there are such rules as DigestSourceRule and DigestHistoryRule. We’ll cover them in the next paragraph.

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

6.4. Digest Rules

6.4.文摘规则

There are two digest rules in the Passay library: DigestHistoryRule and DigestSourceRule. Digest rules are intended to work with passwords stored as digest or hash. Hence, to define them we need to provide an EncodingHashBean object.

Passay库中有两个摘要规则。DigestHistoryRuleDigestSourceRule摘要规则旨在与以摘要或哈希形式存储的密码一起工作。因此,为了定义它们,我们需要提供一个EncodingHashBean对象。

Let’s see how it’s done:

让我们看看它是如何做到的。

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

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

This time we create HistoricalReference by a label and the encoded password to the constructor. After that, we’ve instantiated EncodingHashBean with the proper Codec and digest algorithm.

这次我们通过一个标签和编码后的密码来创建HistoricalReference的构造函数。之后,我们用适当的Codec和摘要算法实例化了EncodingHashBean

Additionally, we can specify the number of iterations and whether the algorithm is salted.

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

Once, we have an encoding bean, we can validate our digest password:

一旦,我们有了一个编码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());

We can learn more about EncodingHashinBean at Cryptacular library webpage.

我们可以在Cryptacular库网页上了解更多关于EncodingHashinBean

6.5. RepeatCharacterRegexRule

6.5.RepeatCharacterRegexRule

Another interesting validation rule is RepeatCharacterRegexRule. We can use it to check whether password contains repeating ASCII characters.

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

Here’s a sample code:

下面是一个示例代码。

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. UsernameRule

6.6.用户名规则

The last rule we’re going to discuss in this chapter is UsernameRule. It enables us to prohibit using the user’s name in the password. 

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

As we’ve learned before, we should store the username in PasswordData:

正如我们之前学到的,我们应该将用户名存储在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. Customized Messages

7.定制的信息

Passay library enables us to customize messages returned by validation rules. Firstly, we should define the messages and assign them to error codes.

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

We can put them into a simple file. Let’s see how easy it is:

我们可以把它们放到一个简单的文件中。让我们看看这有多容易。

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

Once we have messages, we have to load that file. Finally, we can pass it into PasswordValidator object.

一旦我们有了信息,我们就必须加载该文件。最后,我们可以把它传递给PasswordValidator对象。

Here is a sample code:

下面是一个示例代码。

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

MessageResolver resolver = new PropertiesMessageResolver(props);

As we can see, we’ve loaded the message.properties file and passed it into Properties object. Then, we can use the Properties object to create PropertiesMessageResolver.

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

Let’s take a look at the example how to use the message resolver:

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

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));

The example clearly shows that we can translate all error codes with the validator equipped with a message resolver.

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

8. Conclusion

8.结语

In this tutorial, we’ve learned how to use Passay library. We have analyzed several examples of how the library can be easily used for password validation. Provided rules cover most of the common ways of assuring that a password is safe.

在本教程中,我们已经学会了如何使用Passay库。我们分析了几个例子,说明该库可以轻松用于密码验证。所提供的规则涵盖了保证密码安全的大多数常见方法。

But we should remember that Passay library itself doesn’t make our password secure. Firstly, we should learn what are general rules and then use the library to implement them.

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

All examples, as always, can be found over on GitHub.

像往常一样,所有的例子都可以在GitHub上找到

 

1.介绍

1.介绍

如今,大多数网络应用都有自己的密码策略–简单地说,就是为了强迫用户创建难以破解的密码而创建的。

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

2.Maven的依赖性

2.Maven的依赖性

如果我们想在项目中使用Passay库,有必要在我们的pom.xml中添加以下依赖项:

<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<版本>1.3.1</版本>
</dependency>/code>
<p>我们可以在这里找到<a href="https://search.maven.org/search?q=g:org.passay%20AND%20a:passay&core=gav" rel="noopener"> </a>。
</p><h2 class="origin_text" data-id="password-validation" data-trans-id="h2-11">3.密码验证</h2><h2 class="translated_text" data-id="password-validation" data-trans-id="h2-11">3.密码验证</h2>。
<div class="bd-anchor" id="password-validation"></div>
<p>密码验证是Passay库所提供的两个主要功能之一。它是毫不费力和直观的。让我们来发现它。</p>
<h3 class="origin_text" data-id="1-passworddata" data-trans-id="h3-13">3.1.<em>密码数据(PasswordData)</em></h3><h3 class="translated_text" data-id="1-passworddata" data-trans-id="h3-13">3.1.<em>密码数据(PasswordData)</em></h3>。
<div class="bd-anchor" id="1-passworddata"></div>
<p>为了验证我们的密码,我们应该使用<em>PasswordData。</em><strong>它是验证所需信息的容器。</strong>它可以存储这样的数据:</p>
<ul> <li>密码
</li><li>密码</li>
<li>用户名</li>
<li>密码参考列表</li>
<li>起源</li>
</ul>
<p>密码和用户名属性自己解释。Passay库为我们提供了<em>HistoricalReference</em>和<em>SourceReference</em>,我们可以将其添加到密码引用列表中。
</p><p>我们可以使用起源字段来保存关于密码是由用户生成还是定义的信息。
</p><h3 class="origin_text" data-id="2-passwordvalidator" data-trans-id="h3-14">3.2.<em>PasswordValidator</em></h3><h3 class="translated_text" data-id="2-passwordvalidator" data-trans-id="h3-14">3.2.<em>PasswordValidator</em></h3>。
<div class="bd-anchor" id="2-passwordvalidator"></div>
<p><strong>我们应该知道,我们需要<em>PasswordData</em>和<em>PasswordValidator</em>对象来开始验证密码。</strong>我们已经讨论过<em>PasswordData</em>。现在让我们来创建<em>PasswordValidator</em>。
</p><p>首先,我们应该为密码验证定义一组规则。我们必须在创建<em>PasswordValidator</em>对象时将它们传递给构造函数:</p> <p>首先,我们应该定义一组密码验证规则。
</p><pre><code class="language-java">PasswordValidator passwordValidator = new PasswordValidator(new LengthRule(5));

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

PasswordData passwordData = new PasswordData("1234")。

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

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

RuleResult validate = passwordValidator.validate(passwordData);

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

3.3.RuleResult

3.3.RuleResult

RuleResult持有关于一个验证过程的有趣信息。它是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.密码生成

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另一个有趣的事实是,该库为我们提供了EnglishCharacterData。它是一个包含五组字符的枚举:

  • 数位
  • 小写英文字母
  • 大写英文字母
  • 大写英文字母
  • 小写英文字母
  • 小写和大写的组合
  • 特殊字符
  • 然而,没有什么可以阻止我们定义我们的字符集。这就像实现CharacterData接口一样简单。让我们来看看我们如何做到这一点:

    CharacterRule specialCharacterRule = new CharacterRule(new CharacterData() {
        @Override
        public String getErrorCode() {
            return "SAMPLE_ERROR_CODE";
        }
    
        @Override
        public String getCharacters() {
            返回 "ABCxyz123!@#";
        }
    });
    
    PasswordGenerator passwordGenerator = new PasswordGenerator();
    String password = passwordGenerator.generatePassword(10, specialCharacterRule);
    
    Assert.assertTrue(containsOnlyCharactersFromSet(password, "ABCxyz123!@#"));

    5.正向匹配规则

    5.正向匹配规则

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

    首先,我们来了解一下什么是正向规则以及我们如何使用它们。

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

    有六种正向匹配规则:

    正向匹配规则有六种。

    • AllowedCharacterRule – 定义了密码必须包含的所有字符
    • AllowedRegexRule – 定义了密码必须匹配的正则表达式
    • CharacterRule – 定义一个字符集和密码中应包含的最小字符数
    • LengthRule – 定义了密码的最小长度
    • CharacterCharacteristicsRule – 检查密码是否符合N的定义规则。
    • LengthComplexityRule – 允许我们为不同的密码长度定义不同的规则

    5.1.简单的正向匹配规则

    5.1.简单的正向匹配规则

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

    下面是一个讨论过的规则的简短例子:

    PasswordValidator passwordValidator = new PasswordValidator(
    new AllowedCharacterRule(new char[] { 'a', 'b', 'c' }),
    new CharacterRule(EnglishCharacterData.LowerCase, 5),
    新的长度规则(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.CharacterCharacterisitcsRule

    5.2.CharacterCharacterisitcsRule

    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)
    );

    呈现的CharacterCharacteristicsRule要求密码包含四个所提供的规则中的三个。

    5.3.LengthComplexityRule

    5.3.LengthComplexityRule

    另一方面,Passay库为我们提供了LengthComplexityRule它允许我们定义哪些规则应该应用于哪个长度的密码。CharacterCharacteristicsRule相比,它们允许我们使用所有种类的规则–不仅仅是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' });

    正如我们所看到的,对于有1到5个字符的密码,我们应用CharacterRule。但是对于包含6到10个字符的密码,我们希望该密码符合AllowedCharacterRule

    6.否定式匹配规则

    6.否定式匹配规则

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

    让我们来了解一下什么是负向匹配规则:

  • 不合法的密码
  • IllegalCharacterRule – 定义了密码中不能包含的所有字符
  • IllegalRegexRule – 定义了一个不能匹配的正则表达式
  • IllegalSequenceRule – 检查一个密码是否有非法的字符序列
  • NumberRangeRule – 定义一个密码不能包含的数字范围
  • WhitespaceRule – 检查一个密码是否包含空白处
  • DictionaryRule – 检查一个密码是否等于任何字典记录
  • DictionarySubstringRule – 检查一个密码是否包含任何字典记录
  • HistoryRule – 检查一个密码是否包含任何历史密码参考
  • DigestHistoryRule – 检查一个密码是否包含任何消化的历史密码参考
  • SourceRule – 检查一个密码是否包含任何源密码参考
  • DigestSourceRule – 检查一个密码是否包含任何摘要源密码参考
  • UsernameRule – 检查一个密码是否包含一个用户名
  • RepeatCharacterRegexRule – 检查一个密码是否包含重复的ASCII字符
  • 6.1.简单的负数匹配规则

    6.1.简单的负数匹配规则

    首先,我们要看看如何使用简单的规则,如IllegalCharacterRuleIllegalRegexRule,等等。

    PasswordValidator passwordValidator = new PasswordValidator(
      new IllegalCharacterRule(new char[] { 'a' }),
      new NumberRangeRule(1, 10),
      新的空白规则()
    );
    
    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.字典规则

    6.2.字典规则

    如果我们想检查一个密码是否与所提供的单词不相等,该怎么办?

    为此,Passay库为我们提供了优秀的工具。让我们来发现DictionaryRuleDictionarySubstringRule

    WordListDictionary wordListDictionary = new WordListDictionary(
      new ArrayWordList(new String[] { "bar", "foobar" })。
    
    DictionaryRule dictionaryRule = new DictionaryRule(wordListDictionary);
    DictionarySubstringRule dictionarySubstringRule = new DictionarySubstringRule(wordListDictionary);

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

    在现实生活中,我们可以通过字典规则来提供被禁止的词汇。

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

    6.3.HistoryRuleSourceRule

    6.3.HistoryRuleSourceRule

    此外,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 = new PasswordValidator(
      historyRule, sourceRule);

    HistoryRules帮助我们检查一个密码是否曾经被使用过。因为这种做法是不安全的,我们不希望用户使用旧的密码。

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

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

    6.4.文摘规则

    6.4.文摘规则

    Passay库中有两个摘要规则。DigestHistoryRuleDigestSourceRule摘要规则旨在与以摘要或哈希形式存储的密码一起工作。因此,为了定义它们,我们需要提供一个EncodingHashBean对象。

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

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

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

    这次我们通过一个标签和编码后的密码来创建HistoricalReference的构造函数。之后,我们用适当的Codec和摘要算法实例化了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库的网页上了解更多关于EncodingHashinBean的信息

    6.5.RepeatCharacterRegexRule

    6.5.RepeatCharacterRegexRule

    另一个有趣的验证规则是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.用户名规则

    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.自定义消息

    7.自定义消息

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

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

    我们可以把它们放到一个简单的文件中。

    TOO_LONG=Password must not have more characters than %2$s.
    TOO_SHORT=密码不能少于%2$s.

    一旦我们有了信息,我们必须加载该文件。最后,我们可以把它传递给PasswordValidator对象。

    下面是一个示例代码:

    URL resource = this.getClass().getClassLoader().getResource("mages.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"))。
    规则结果 tooLong = validator.validate(new PasswordData("ZZZZZZZZ"))。
    
    Assert.assertEquals(
      "密码不能包含少于16个字符。" 。
      validator.getMessages(tooShort).get(0))。
    Assert.assertEquals(
      "密码不能包含多于16个字符"。
      validator.getMessages(tooLong).get(0));

    这个例子清楚地表明,我们可以用装有消息解析器的验证器来翻译所有的错误代码。

    8、结论

    8、结论

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

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

    所有的例子都可以在GitHub上找到