许多在线网络平台要求用户在注册时输入强密码。此策略有助于减少用户数据对任何黑客攻击的脆弱性。

在本文中,我们将创建一个带有注册页面的简单表单。在继续本教程之前,您应该对使用 Spring 框架的 Java 有基本的了解。

什么是帕赛?

Passay是一个基于Java的密码生成和验证库。它建立在成功的基础上,并提供了一个全面且可扩展的功能集。vt-password

技术栈

  • 节点.js。
  • 角度 9.
  • 弹簧启动 2.
  • Maven 3.6.1.
  • 爪哇 8.
  • 吉特。

Maven Dependency

使用 Spring Initializr 生成具有以下依赖项的 Spring boot 2 项目:web、lombok、spring-boot-starter-validation。

然后添加 Passay 依赖项以管理验证策略。


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

您可以在此处找到所有版本。


使用包含要验证的信息的类。UserData


package com.passay.sample.custompasswordvalidation.model;

import com.passay.sample.custompasswordvalidation.annotation.PasswordValueMatch;
import com.passay.sample.custompasswordvalidation.annotation.ValidPassword;
import lombok.*;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

/**
 * <h2>UserData</h2>
 *
 * @author aek
 */
@PasswordValueMatch.List({
        @PasswordValueMatch(
                field = "password",
                fieldMatch = "confirmPassword",
                message = "Passwords do not match!"
        )
})
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class UserData {


    @NonNull
    @NotBlank(message = "username is mandatory")
    private String username;

    @NotNull
    @NotEmpty
    @Email
    private String email;


    @ValidPassword
    @NonNull
    @NotBlank(message = "New password is mandatory")
    private String password;


    @ValidPassword
    @NonNull
    @NotBlank(message = "Confirm Password is mandatory")
    private String confirmPassword;
}

两个重要的注释:


  • @PasswordValueMatch:检查密码和确认密码是否匹配。
  • @ValidPassword:包含密码验证策略。

密码验证

密码验证涉及从规则集创建 a,它只是一个对象列表。注释是由PasswordValidatorRule@ValidPasswordPasswordConstraintValidator.class


package com.passay.sample.custompasswordvalidation.annotation;



import com.passay.sample.custompasswordvalidation.utils.PasswordConstraintValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


/**
 * <h2>ValidPassword</h2>
 *
 * @author aek
 */
@Documented
@Constraint(validatedBy = PasswordConstraintValidator.class)
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface ValidPassword {

    String message() default "Invalid Password";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

请考虑以下简单密码策略:


  • 密码长度应在 8 到 16 个字符之间。
  • 密码不应包含任何空格。
  • 密码必须至少包含 1 个大写字符。
  • 密码必须至少包含 1 个小写字符。
  • 密码必须至少包含 1 位数字字符。
  • 密码必须至少包含 1 个符号(特殊字符)。
  • 拒绝包含按字母顺序排列的 >= 5 个字符序列的密码(例如 abcdef)。
  • 拒绝包含 >= 5 个字符的数字序列(例如 12345)的密码。

该类包含所有以前定义的密码规则,而无需手动实现它们。PasswordConstraintValidator


package com.passay.sample.custompasswordvalidation.utils;

import com.passay.sample.custompasswordvalidation.annotation.ValidPassword;
import lombok.SneakyThrows;
import org.passay.*;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;


/**
 * <h2>PasswordConstraintValidator</h2>
 *
 * @author aek
 */
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {

    @Override
    public void initialize(final ValidPassword arg0) {

    }

    @SneakyThrows
    @Override
    public boolean isValid(String password, ConstraintValidatorContext context) {

        //customizing validation messages
        Properties props = new Properties();
        InputStream inputStream = getClass()
                .getClassLoader().getResourceAsStream("passay.properties");
        props.load(inputStream);
        MessageResolver resolver = new PropertiesMessageResolver(props);

        PasswordValidator validator = new PasswordValidator(resolver, Arrays.asList(

                // length between 8 and 16 characters
                new LengthRule(8, 16),

                // at least one upper-case character
                new CharacterRule(EnglishCharacterData.UpperCase, 1),

                // at least one lower-case character
                new CharacterRule(EnglishCharacterData.LowerCase, 1),

                // at least one digit character
                new CharacterRule(EnglishCharacterData.Digit, 1),

                // at least one symbol (special character)
                new CharacterRule(EnglishCharacterData.Special, 1),

                // no whitespace
                new WhitespaceRule(),

                // rejects passwords that contain a sequence of >= 5 characters alphabetical  (e.g. abcdef)
                 new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),
                // rejects passwords that contain a sequence of >= 5 characters numerical   (e.g. 12345)
                new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false)
        ));

        RuleResult result = validator.validate(new PasswordData(password));

        if (result.isValid()) {
            return true;
        }

        List<String> messages = validator.getMessages(result);
        String messageTemplate = String.join(",", messages);
        context.buildConstraintViolationWithTemplate(messageTemplate)
                .addConstraintViolation()
                .disableDefaultConstraintViolation();
        return false;
    }

}

Passay 列出了几个规则来帮助验证密码。可以使用Passay编写的规则的完整列表可以在官方网站上找到。


除了密码验证之外,passay 还允许您使用给定的策略生成密码。

创建以捕获将为数据验证引发的所有异常。BaseExceptionHandler.class


package com.passay.sample.custompasswordvalidation.web;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {


    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse handleValidationExceptions(MethodArgumentNotValidException ex) {

        Map<String, String> errors = new HashMap<>();

        ex.getBindingResult().getFieldErrors().forEach(error -> {
                    if (errors.containsKey(error.getField())) {
                        errors.put(error.getField(), String.format("%s, %s", errors.get(error.getField()), error.getDefaultMessage()));
                    } else {
                        errors.put(error.getField(), error.getDefaultMessage());
                    }
                }
        );
        return new ApiResponse(errors, "VALIDATION_FAILED");
    }
}

启动后端项目。http://localhost:8080/


在本文中,我创建了一个使用Angular作为前端的注册表单。

现在,使用命令行在文件夹下添加 Angular 项目:src/main

ng new webapp

添加注册表单代码的内容后。运行前端npm start

打开浏览器并输入无效的密码以验证验证是否正常工作。http://localhost:4200/

springboot 数据验证 数字范围 springboot验证器_spring

完整的源代码可以在我的GitHub 存储库中找到。