飞道的博客

Spring Boot 使用 Passay 库的自定义密码验证器

579人阅读  评论(0)

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

在本文中,我们将创建一个带有注册页面的简单表单。在继续本教程之前,您应该对使用 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 依赖项以管理验证策略。


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


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

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


    
  1. package com.passay.sample.custompasswordvalidation.model;
  2. import com.passay.sample.custompasswordvalidation.annotation.PasswordValueMatch;
  3. import com.passay.sample.custompasswordvalidation.annotation.ValidPassword;
  4. import lombok.*;
  5. import javax.validation.constraints.Email;
  6. import javax.validation.constraints.NotBlank;
  7. import javax.validation.constraints.NotEmpty;
  8. import javax.validation.constraints.NotNull;
  9. /**
  10. * <h2>UserData</h2>
  11. *
  12. * @author aek
  13. */
  14. @PasswordValueMatch.List({
  15. @PasswordValueMatch(
  16. field = "password",
  17. fieldMatch = "confirmPassword",
  18. message = "Passwords do not match!"
  19. )
  20. })
  21. @AllArgsConstructor
  22. @NoArgsConstructor
  23. @Getter
  24. @Setter
  25. @ToString
  26. public class UserData {
  27. @NonNull
  28. @NotBlank(message = "username is mandatory")
  29. private String username;
  30. @NotNull
  31. @NotEmpty
  32. @Email
  33. private String email;
  34. @ValidPassword
  35. @NonNull
  36. @NotBlank(message = "New password is mandatory")
  37. private String password;
  38. @ValidPassword
  39. @NonNull
  40. @NotBlank(message = "Confirm Password is mandatory")
  41. private String confirmPassword;
  42. }


两个重要的注释:

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

密码验证

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


      
  1. package com.passay.sample.custompasswordvalidation.annotation;
  2. import com.passay.sample.custompasswordvalidation.utils.PasswordConstraintValidator;
  3. import javax.validation.Constraint;
  4. import javax.validation.Payload;
  5. import java.lang.annotation.Documented;
  6. import java.lang.annotation.Retention;
  7. import java.lang.annotation.Target;
  8. import static java.lang.annotation.ElementType.*;
  9. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  10. /**
  11. * <h2>ValidPassword</h2>
  12. *
  13. * @author aek
  14. */
  15. @Documented
  16. @Constraint(validatedBy = PasswordConstraintValidator.class)
  17. @Target({ TYPE, FIELD, ANNOTATION_TYPE })
  18. @Retention(RUNTIME)
  19. public @interface ValidPassword {
  20. String message () default "Invalid Password";
  21. Class<?>[] groups() default {};
  22. Class<? extends Payload>[] payload() default {};
  23. }


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

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

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


      
  1. package com.passay.sample.custompasswordvalidation.utils;
  2. import com.passay.sample.custompasswordvalidation.annotation.ValidPassword;
  3. import lombok.SneakyThrows;
  4. import org.passay.*;
  5. import javax.validation.ConstraintValidator;
  6. import javax.validation.ConstraintValidatorContext;
  7. import java.io.InputStream;
  8. import java.util.Arrays;
  9. import java.util.List;
  10. import java.util.Properties;
  11. /**
  12. * <h2>PasswordConstraintValidator</h2>
  13. *
  14. * @author aek
  15. */
  16. public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {
  17. @Override
  18. public void initialize (final ValidPassword arg0) {
  19. }
  20. @SneakyThrows
  21. @Override
  22. public boolean isValid (String password, ConstraintValidatorContext context) {
  23. //customizing validation messages
  24. Properties props = new Properties();
  25. InputStream inputStream = getClass()
  26. .getClassLoader().getResourceAsStream( "passay.properties");
  27. props.load(inputStream);
  28. MessageResolver resolver = new PropertiesMessageResolver(props);
  29. PasswordValidator validator = new PasswordValidator(resolver, Arrays.asList(
  30. // length between 8 and 16 characters
  31. new LengthRule( 8, 16),
  32. // at least one upper-case character
  33. new CharacterRule(EnglishCharacterData.UpperCase, 1),
  34. // at least one lower-case character
  35. new CharacterRule(EnglishCharacterData.LowerCase, 1),
  36. // at least one digit character
  37. new CharacterRule(EnglishCharacterData.Digit, 1),
  38. // at least one symbol (special character)
  39. new CharacterRule(EnglishCharacterData.Special, 1),
  40. // no whitespace
  41. new WhitespaceRule(),
  42. // rejects passwords that contain a sequence of >= 5 characters alphabetical (e.g. abcdef)
  43. new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),
  44. // rejects passwords that contain a sequence of >= 5 characters numerical (e.g. 12345)
  45. new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false)
  46. ));
  47. RuleResult result = validator.validate( new PasswordData(password));
  48. if (result.isValid()) {
  49. return true;
  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. }


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

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

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


      
  1. package com.passay.sample.custompasswordvalidation.web;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.http.HttpStatus;
  4. import org.springframework.web.bind.MethodArgumentNotValidException;
  5. import org.springframework.web.bind.annotation.ExceptionHandler;
  6. import org.springframework.web.bind.annotation.ResponseStatus;
  7. import org.springframework.web.bind.annotation.RestControllerAdvice;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. @Slf4j
  11. @RestControllerAdvice
  12. public class BaseExceptionHandler {
  13. @ResponseStatus(HttpStatus.BAD_REQUEST)
  14. @ExceptionHandler(MethodArgumentNotValidException.class)
  15. public ApiResponse handleValidationExceptions (MethodArgumentNotValidException ex) {
  16. Map<String, String> errors = new HashMap<>();
  17. ex.getBindingResult().getFieldErrors().forEach(error -> {
  18. if (errors.containsKey(error.getField())) {
  19. errors.put(error.getField(), String.format( "%s, %s", errors.get(error.getField()), error.getDefaultMessage()));
  20. } else {
  21. errors.put(error.getField(), error.getDefaultMessage());
  22. }
  23. }
  24. );
  25. return new ApiResponse(errors, "VALIDATION_FAILED");
  26. }
  27. }


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

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

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

ng new webapp

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

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

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

 


转载:https://blog.csdn.net/allway2/article/details/127776646
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场