许多在线网络平台要求用户在注册时输入强密码。此策略有助于减少用户数据对任何黑客攻击的脆弱性。
在本文中,我们将创建一个带有注册页面的简单表单。在继续本教程之前,您应该对使用 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,它只是一个对象列表。注释是由PasswordValidator
Rule
@ValidPassword
PasswordConstraintValidator.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/
完整的源代码可以在我的GitHub 存储库中找到。