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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Simplify Password Validation with Passay: One‑Line Security in Java

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., Kkmv6K387V0x

Conclusion

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMavenSpring BootSecurityPassword ValidationPassay
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.