DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4
  • How Spring Boot Starters Integrate With Your Project
  • A Practical Guide to Creating a Spring Modulith Project
  • Structured Logging in Spring Boot 3.4 for Improved Logs

Trending

  • Edge Computing in Utility IoT: Two Architecture Patterns That Actually Work
  • Stop Running Two Data Systems for One Agent Query
  • Integrating AI-Driven Decision-Making in Agile Frameworks: A Deep Dive into Real-World Applications and Challenges
  • Implementing Secure API Gateways for Microservices Architecture
  1. DZone
  2. Coding
  3. Frameworks
  4. Spring Boot - Custom Password Validator Using Passay Library

Spring Boot - Custom Password Validator Using Passay Library

By 
Anicet Eric user avatar
Anicet Eric
·
Sep. 02, 20 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
38.1K Views

Join the DZone community and get the full member experience.

Join For Free

Many online web platforms require users to enter a strong password during registration. This strategy helps reduce the vulnerability of user data to any hacking.

In this article, we'll create a simple form with a registration page. Before continuing with this tutorial, you should have a basic understanding of Java with the Spring framework.

What Is Passay ?

Passay is a Java-based password generation and validation library. It builds on the success of vt-password and provides a comprehensive and extensible feature set.

Technology Stack

  • Node.js.
  • Angular 9.
  • Spring Boot 2.
  • Maven 3.6.1.
  • JAVA 8.
  • Git.

Maven Dependency

Use Spring Initializr to generate the spring boot 2 project with the dependencies: web, lombok, spring-boot-starter-validation. 

Then add the Passay dependency to manage validation policies.

XML
xxxxxxxxxx
1
 
1
  <dependency>
2
      <groupId>org.passay</groupId>
3
      <artifactId>passay</artifactId>
4
      <version>1.6.0</version>
5
  </dependency>


You can find all versions here.

Use the UserData class containing the information to verify.

Java
xxxxxxxxxx
1
36
 
1
@PasswordValueMatch.List({
2
        @PasswordValueMatch(
3
                field = "password",
4
                fieldMatch = "confirmPassword",
5
                message = "Passwords do not match!"
6
        )
7
})
8
@AllArgsConstructor
9
@NoArgsConstructor
10
@Getter
11
@Setter
12
@ToString
13
public class UserData {
14
15
16
    @NonNull
17
    @NotBlank(message = "username is mandatory")
18
    private String username;
19
20
    @NotNull
21
    @NotEmpty
22
    @Email
23
    private String email;
24
25
26
    @ValidPassword
27
    @NonNull
28
    @NotBlank(message = "New password is mandatory")
29
    private String password;
30
31
32
    @ValidPassword
33
    @NonNull
34
    @NotBlank(message = "Confirm Password is mandatory")
35
    private String confirmPassword;
36
}


Two important annotations:

  • @PasswordValueMatch: Check if the password and confirmation password match.
  • @ValidPassword: Contains the password validation policy.

Password Validation

Password validation involves creating a PasswordValidator from a rule set, which is simply a list of Rule objects . The @ValidPassword annotation is an annotation validated by the PasswordConstraintValidator.class

Java
xxxxxxxxxx
1
12
 
1
@Documented
2
@Constraint(validatedBy = PasswordConstraintValidator.class)
3
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
4
@Retention(RUNTIME)
5
public @interface ValidPassword {
6
7
    String message() default "Invalid Password";
8
9
    Class<?>[] groups() default {};
10
11
    Class<? extends Payload>[] payload() default {};
12
}


Consider the following simple password policy:

  • Length of password should be in between 8 to 16 characters. 
  • A password should not contain any whitespace.
  • A password must contain at least 1 upper-case character.
  • A password must contain at least 1 lower-case character.
  • A password must contain at least 1 digit character.
  • A password must contain at least 1 symbol (special character).
  • Rejects passwords that contain a sequence of >= 5 characters alphabetical  (e.g. abcdef).
  • Rejects passwords that contain a sequence of >= 5 characters numerical   (e.g. 12345).

The PasswordConstraintValidator class contains all previously defined password rules without having to implement them manually.

Java
xxxxxxxxxx
1
59
 
1
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {
2
3
    @Override
4
    public void initialize(final ValidPassword arg0) {
5
6
    }
7
8
    @SneakyThrows
9
    @Override
10
    public boolean isValid(String password, ConstraintValidatorContext context) {
11
12
        //customizing validation messages
13
        Properties props = new Properties();
14
        InputStream inputStream = getClass()
15
                .getClassLoader().getResourceAsStream("passay.properties");
16
        props.load(inputStream);
17
        MessageResolver resolver = new PropertiesMessageResolver(props);
18
19
        PasswordValidator validator = new PasswordValidator(resolver, Arrays.asList(
20
21
                // length between 8 and 16 characters
22
                new LengthRule(8, 16),
23
24
                // at least one upper-case character
25
                new CharacterRule(EnglishCharacterData.UpperCase, 1),
26
27
                // at least one lower-case character
28
                new CharacterRule(EnglishCharacterData.LowerCase, 1),
29
30
                // at least one digit character
31
                new CharacterRule(EnglishCharacterData.Digit, 1),
32
33
                // at least one symbol (special character)
34
                new CharacterRule(EnglishCharacterData.Special, 1),
35
36
                // no whitespace
37
                new WhitespaceRule(),
38
39
                // rejects passwords that contain a sequence of >= 5 characters alphabetical  (e.g. abcdef)
40
                 new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),
41
                // rejects passwords that contain a sequence of >= 5 characters numerical   (e.g. 12345)
42
                new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false)
43
        ));
44
45
        RuleResult result = validator.validate(new PasswordData(password));
46
47
        if (result.isValid()) {
48
            return true;
49
        }
50
51
        List<String> messages = validator.getMessages(result);
52
        String messageTemplate = String.join(",", messages);
53
        context.buildConstraintViolationWithTemplate(messageTemplate)
54
                .addConstraintViolation()
55
                .disableDefaultConstraintViolation();
56
        return false;
57
    }
58
59
}


passay has a list of several rules to help validate passwords. The full list of rules that can be written using Passay can be found on the official website. 

In addition to password validation, passay allows you to generate a password using a given policy.

Create BaseExceptionHandler.class to catch all exceptions that will be thrown for data validation.

Java
xxxxxxxxxx
1
22
 
1
@Slf4j
2
@RestControllerAdvice
3
public class BaseExceptionHandler {
4
5
6
    @ResponseStatus(HttpStatus.BAD_REQUEST)
7
    @ExceptionHandler(MethodArgumentNotValidException.class)
8
    public ApiResponse handleValidationExceptions(MethodArgumentNotValidException ex) {
9
10
        Map<String, String> errors = new HashMap<>();
11
12
        ex.getBindingResult().getFieldErrors().forEach(error -> {
13
                    if (errors.containsKey(error.getField())) {
14
                        errors.put(error.getField(), String.format("%s, %s", errors.get(error.getField()), error.getDefaultMessage()));
15
                    } else {
16
                        errors.put(error.getField(), error.getDefaultMessage());
17
                    }
18
                }
19
        );
20
        return new ApiResponse(errors, "VALIDATION_FAILED");
21
    }
22
}


Launch the Backend project. http://localhost:8080/

For this article, I created a signup form with Angular for the front-end. 

Now, Add the Angular project under the src/main folder using the command line:

ng new webapp

After adding the content of the registration form code. run frontend npm start

Open your browser on http://localhost:4200/ and enter an invalid password to verify that validation is working.

Registration error on login


The complete source code can be found in my GitHub repository.

Spring Framework Spring Boot Library

Opinions expressed by DZone contributors are their own.

Related

  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4
  • How Spring Boot Starters Integrate With Your Project
  • A Practical Guide to Creating a Spring Modulith Project
  • Structured Logging in Spring Boot 3.4 for Improved Logs

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook