Simplify Password Validation with Passay: One‑Line Security in Java
This article shows how the open‑source Passay library replaces hand‑written password regex with a unified, extensible rule set, covering dependency setup, rule definitions, custom messages, N‑of‑M character requirements, history checks, and password generation, all demonstrated with runnable Java code.
Introduction
Many projects perform password checks by only validating length or by writing a few ad‑hoc regular expressions, which makes the rules scattered, hard to maintain, and error‑prone when policies change. Passay is an open‑source Java library that offers a rich collection of built‑in password rules and generation capabilities, enabling a unified, flexible, and maintainable password‑security strategy.
Key Features
Comprehensive built‑in rule set for common password policies.
Ability to generate compliant passwords automatically.
Customizable validation messages.
Internationalization support for multiple languages.
Full Unicode character support.
All components are thread‑safe.
Core Components
Passay’s API revolves around three core components:
Rule : One or more rules that together form a password‑policy rule set.
PasswordValidator : Validates a password against a rule set.
PasswordGenerator : Generates passwords that satisfy a rule set.
Rule Types
Rules are divided into two categories.
Positive matching rules (must satisfy) :
AllowedCharacterRule – require inclusion of all characters from a specified set.
AllowedRegexRule – require the password to match a given regular expression.
CharacterCharacteristicsRule – require the password to contain at least M of N character classes (e.g., 3 of 4: upper, lower, digit, special).
CharacterRule – require at least N characters from a specific class.
LengthRule – enforce a minimum and/or maximum length.
LengthComplexityRule – apply different constraints based on length (e.g., 8‑12 characters must contain digit and special; 13+ characters may be letters only).
Negative matching rules (must not match) :
DictionaryRule, DictionarySubstringRule, DigestDictionaryRule – reject passwords that appear in a dictionary (exact, substring, or hashed).
HistoryRule, DigestHistoryRule – reject passwords that were used previously (plain or hashed).
CharacterOccurrencesRule – reject passwords with excessive repetitions of a single character.
IllegalCharacterRule – reject passwords containing characters from a prohibited set.
IllegalRegexRule – reject passwords matching a prohibited regular expression.
IllegalSequenceRule – reject sequential characters of length ≥ N (alphabetic, numeric, keyboard).
NumberRangeRule – reject passwords containing numbers within a forbidden range.
SourceRule, DigestSourceRule – reject passwords that match entries from an external data source (plain or hashed).
RepeatCharactersRule – reject passwords with multiple repeated character segments.
UsernameRule – reject passwords that contain the user’s username (or its reverse).
WhitespaceRule – reject passwords that contain spaces, tabs, or other whitespace.
Using Passay
Add the library to your Maven project:
<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<version>2.0.0</version>
</dependency>PasswordValidator Example
The following code builds a validator for a simple policy: length 8‑16, at least one uppercase, one lowercase, one digit, one special character, no whitespace, and disallow common sequential patterns.
PasswordValidator validator = new DefaultPasswordValidator(
// length 8‑16 characters
new LengthRule(8, 16),
// at least one uppercase letter
new CharacterRule(EnglishCharacterData.UpperCase, 1),
// at least one lowercase letter
new CharacterRule(EnglishCharacterData.LowerCase, 1),
// at least one digit
new CharacterRule(EnglishCharacterData.Digit, 1),
// at least one special symbol
new CharacterRule(EnglishCharacterData.Special, 1),
// illegal alphabetical sequence of length ≥5 (e.g., abcde)
new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),
// illegal numeric sequence of length ≥5 (e.g., 34567)
new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false),
// illegal QWERTY keyboard sequence of length ≥5 (e.g., asdfg)
new IllegalSequenceRule(EnglishSequenceData.USQwerty, 5, false),
// disallow any whitespace character
new WhitespaceRule()
);
Scanner scanner = new Scanner(System.in);
boolean passOk = false;
do {
System.out.print("Password: ");
String pwdStr = scanner.nextLine();
char[] password = pwdStr.toCharArray();
ValidationResult result = validator.validate(new PasswordData(new UnicodeString(password)));
if (result.isValid()) {
System.out.println("密码合规");
passOk = true;
} else {
for (String msg : result.getMessages()) {
System.out.println(msg);
}
System.out.println("请重新输入符合规则的密码!
");
}
} while (!passOk);
scanner.close();Sample interaction:
Password: 123123
请重新输入符合规则的密码!
Password: 123!
请重新输入符合规则的密码!
Password: 123123aX!
密码合规Custom Validation Messages
Create a passay.properties file under src/main/resources to override default messages, for example:
HISTORY_VIOLATION = 该密码与您历史使用过的 %1$s 个旧密码其中之一重复。
ILLEGAL_WORD = 密码中包含字典禁用词条「%1$s」。
ILLEGAL_SEQUENCE = 密码包含非法键盘连续序列「%1$s」。
WHITESPACE = 密码中存在空白字符。Load the properties and pass a MessageResolver to the validator:
Properties props = new Properties();
props.load(new ClassPathResource("passay.properties").getInputStream());
MessageResolver resolver = new PropertiesMessageResolver(props);
PasswordValidator validator = new DefaultPasswordValidator(resolver,
// same rules as before …
);Running the validator now produces the custom Chinese messages shown in the source.
Advanced N‑of‑M Rule (CharacterCharacteristicsRule)
Many policies require the password to contain at least M of N character classes. Passay provides CharacterCharacteristicsRule for this scenario.
LengthRule r1 = new LengthRule(8, 16);
CharacterCharacteristicsRule r2 = new CharacterCharacteristicsRule(3,
new CharacterRule(EnglishCharacterData.UpperCase, 1),
new CharacterRule(EnglishCharacterData.LowerCase, 1),
new CharacterRule(EnglishCharacterData.Digit, 1),
new CharacterRule(EnglishCharacterData.Special, 1));
WhitespaceRule r3 = new WhitespaceRule();
PasswordValidator validator = new DefaultPasswordValidator(resolver, r1, r2, r3);
ValidationResult result = validator.validate(new PasswordData(new UnicodeString("123")));
if (!result.isValid()) {
for (String msg : result.getMessages()) {
System.out.println(msg);
}
}Result:
密码长度至少为 8 位。
密码需包含至少 1 个大写英文字母。
密码需包含至少 1 个小写英文字母。
密码需包含至少 1 个特殊符号。
1 到 4 密码规则, 但是必须匹配 3 个.Password History Validation
To prevent reuse of old passwords, Passay offers HistoryRule (plain‑text) and DigestHistoryRule (hashed). The hashed variant is more common because most systems store password hashes.
Add the cryptographic extension:
<dependency>
<groupId>org.passay</groupId>
<artifactId>passay-crypt</artifactId>
<version>2.0.0</version>
</dependency>Example code:
Properties props = new Properties();
props.load(new ClassPathResource("passay.properties").getInputStream());
MessageResolver resolver = new PropertiesMessageResolver(props);
EncodingHashBean hasher = new EncodingHashBean(new CodecSpec("Base64"),
new DigestSpec("SHA256"), 1, false);
List<Rule> rules = Arrays.asList(new DigestHistoryRule(hasher));
PasswordValidator validator = new DefaultPasswordValidator(resolver, rules);
List<Reference> history = Arrays.asList(
new HistoricalReference("SHA256", "j93vuQDT5ZpZ5L9FxSfeh87zznS3CM8govlN…"),
new HistoricalReference("SHA256", "mhR+BHzcQXt2fOUWCy4f903AHA6LzNYKlSOQ…"),
new HistoricalReference("SHA256", "BDr/pEo1eMmJoeP6gRKh6QMmiGAyGcddvfAHH+VJ05iG/…")
);
PasswordData data = new PasswordData("username", "P@ssword1", history);
ValidationResult result = validator.validate(data);
if (result.isValid()) {
System.out.println("密码合规");
} else {
for (String msg : result.getMessages()) {
System.out.println(msg);
}
}Result:
该密码与您历史使用过的 3 个旧密码其中之一重复。PasswordGenerator
Passay can also generate passwords that satisfy a rule set. The generator builds character appenders based on the supplied rules and validates the result.
List<CharacterRule> rules = Arrays.asList(
new CharacterRule(EnglishCharacterData.UpperCase, 1),
new CharacterRule(EnglishCharacterData.LowerCase, 1),
new CharacterRule(EnglishCharacterData.Digit, 1)
);
PasswordGenerator generator = new PasswordGenerator(12, rules);
UnicodeString password = generator.generate();
System.err.println(password); // e.g., Kkmv6K387V0xConclusion
Passay provides a complete, extensible solution for password validation and generation in Java. By replacing hand‑crafted regular expressions with a declarative rule set, developers gain clearer policy definition, easier maintenance, internationalized messages, and built‑in support for advanced scenarios such as N‑of‑M character requirements and historical password checks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
