JSR380验证框架
依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
demo
@Size(min=10,max=200 ,message="描述需要控制在10到200字符")
@Min(value=18,message="年龄不能小于18")
@Max(value=150,message="年龄不能大于150")
list<@NotBlank String> roles; public Optional<@Past localdate> getDateOfBirth(){return Optional.of(dateofBirth)
}
常用的验证注解
- @NotNull 论证注解的属性值不是空的。
- @AssertTrue 验证注解的属性值是否为真。
- @Size 验证注解的属性值的大小介于属性min和max之间;可应用于string,collection,map和数组属性。
- @Min 验证注解属性的值不小于值属性的值
- @Max 验证被注解的属性的值不大于值属性的值
- @Email 验证注解的属性是一个有效的电子邮件地址。
- @Pattern 验证注解的属性是否匹配正则表达式
- @NotEmpty 验证属性不是空或空;可以应用于string,collection,map或array值。
- @NotBlank 只能应用于文本值,并验证该属性不是空的或空白的。
- @Positive 和 @PositiveOrZero 适用于数值,并验证它们是严格意义上的正数,或包括0在内的正数
- @Negative 和 @NegativeOrZero 适用于数值,并脸证它们是严格意上的负值,或包括0在内的负值。
- @Past 和 @PastOrPresent 验证一个日期值是在过去或过去(包括现在),可应用于日期类型,包括在JAVA中新增的日期类型。
- @Futuren 和 @FutureOrPresent 验证一个日期值是在未来,或者说是在未来,包括现在。
基础使用
domain
@Data
public class UserDto implements Serializable {@NotNull@NotBlank@Size(min = 4, max = 50, message = "用户名长度必须在4到50个字符之间")private String username;@NotNull@NotBlank@Size(min = 4, max = 50, message = "用户密码长度必须在4到50个字符之间")private String password;@NotNull@NotBlank@Size(min = 4, max = 50, message = "用户密码长度必须在4到50个字符之间")private String matchingPassword;@NotNull@NotBlank@Email
//正则验证邮箱格式 @Pattern(regexp = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")private String email;@NotNull@NotBlank@Size(min = 4, max = 50, message = "姓名长度必须在4到50个字符之间")private String name;
}
controller
@RestController
@RequestMapping("/authorize")
public class AuthorizeResource {//需要加@Valid使验证生效@PostMapping("/register")public UserDto register(@Valid @RequestBody UserDto userDto) {return userDto;}}
结果
{"username": "zhangsan","password": "qwerty12345T!","matchingPassword": "12345678","email": "zs@local.top","name": "张三李四"
}
{"timestamp": "2024-03-06T08:39:48.102+00:00","status": 400,"error": "Bad Request","trace": " rejected value [张三李四111111111111111111111111111111111111111111111111111111111111111111]; codes [Size.userDto.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDto.name,name]; arguments []; default message [name],50,4]; default message [姓名长度必须在4到50个字符之间]] \r\n\tat ","message": "Validation failed for object='userDto'. Error count: 1","errors": [{"codes": ["Size.userDto.name","Size.name","Size.java.lang.String","Size"],"arguments": [{"codes": ["userDto.name","name"],"arguments": null,"defaultMessage": "name","code": "name"},50,4],"defaultMessage": "姓名长度必须在4到50个字符之间","objectName": "userDto","field": "name","rejectedValue": "张三李四111111111111111111111111111111111111111111111111111111111111111111","bindingFailure": false,"code": "Size"}],"path": "/authorize/register"
}
自定义验证规则
demo1 邮箱验证
1、定义注解
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = EmailValidator.class) //指定验证器类
@Documented
public @interface ValidEmail {String message() default "{ValidEmail.email}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
2、验证器
实现ConstraintValidator
接口
public class EmailValidator implements ConstraintValidator<ValidEmail, String> {private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";@Overridepublic void initialize(final ValidEmail constraintAnnotation) {}@Overridepublic boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {return validateEmail(s);}private boolean validateEmail(String s) {Pattern pattern = Pattern.compile(EMAIL_PATTERN);Matcher matcher = pattern.matcher(s);return matcher.matches();}
}
3、加上注解
@NotNull@NotBlank@ValidEmail(message = "邮箱格式不正确")
// @Email
// @Pattern(regexp = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")private String email;
结果
{"timestamp": "2024-03-06T08:54:06.189+00:00","status": 400,"error": "Bad Request","trace": "codes [userDto.email,email]; arguments []; default message [email]]; default message [邮箱格式不正确]] \r\n\tat ","message": "Validation failed for object='userDto'. Error count: 1","errors": [{"codes": ["ValidEmail.userDto.email","ValidEmail.email","ValidEmail.java.lang.String","ValidEmail"],"arguments": [{"codes": ["userDto.email","email"],"arguments": null,"defaultMessage": "email","code": "email"}],"defaultMessage": "邮箱格式不正确","objectName": "userDto","field": "email","rejectedValue": "zs@local","bindingFailure": false,"code": "ValidEmail"}],"path": "/authorize/register"
}
密码的验证规则
- 密码的验证比较复杂,使用passay框架进行验证
- 封装验证逻辑在注解中,有效的剥离验证逻辑和业务逻辑
- 对于2个以上属性的复合验证,可以写一个应用于类的注解
<dependency><groupId>org.passay</groupId><artifactId>passay</artifactId><version>1.6.0</version>
</dependency>
注解
@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 {};}
验证器
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {@Overridepublic void initialize(ValidPassword constraintAnnotation) {}@Overridepublic boolean isValid(String password, ConstraintValidatorContext constraintValidatorContext) {PasswordValidator validator = new PasswordValidator(List.of(// 长度规则:8 - 30 位new LengthRule(8, 30),// 至少有一个大写字母new CharacterRule(EnglishCharacterData.UpperCase, 1),// 至少有一个小写字母new CharacterRule(EnglishCharacterData.LowerCase, 1),// 至少有一个数字new CharacterRule(EnglishCharacterData.Digit, 1),// 至少有一个特殊字符new CharacterRule(EnglishCharacterData.Special, 1),// 不允许连续 3 个字母,按字母表顺序// alphabetical is of the form 'abcde', numerical is '34567', qwery is 'asdfg'// the false parameter indicates that wrapped sequences are allowed; e.g. 'xyzabc'new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),// 不允许 3 个连续数字new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false),// 不允许 QWERTY 键盘上的三个连续相邻的按键所代表的字符new IllegalSequenceRule(EnglishSequenceData.USQwerty, 5, false),// 不允许包含空格new WhitespaceRule()));RuleResult validate = validator.validate(new PasswordData(password));if (validate.isValid()) {return true;}return false;}
}
domain
@Data
public class UserDto implements Serializable {@NotNull@NotBlank@Size(min = 4, max = 50, message = "用户名长度必须在4到50个字符之间")private String username;@NotNull@ValidPasswordprivate String password;@NotNull@ValidPasswordprivate String matchingPassword;@NotNull@NotBlank@ValidEmail(message = "邮箱格式不正确")// @Email// @Pattern(regexp = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")private String email;@NotNull@NotBlank@Size(min = 4, max = 50, message = "姓名长度必须在4到50个字符之间")private String name;
}
结果
{"timestamp": "2024-03-06T09:23:38.543+00:00","status": 400,"error": "Bad Request","trace": "","message": "Validation failed for object='userDto'. Error count: 3","errors": [{"codes": ["ValidPassword.userDto.password","ValidPassword.password","ValidPassword.java.lang.String","ValidPassword"],"arguments": [{"codes": ["userDto.password","password"],"arguments": null,"defaultMessage": "password","code": "password"}],"defaultMessage": "Invalid Password","objectName": "userDto","field": "password","rejectedValue": "qwerty12345T!","bindingFailure": false,"code": "ValidPassword"},{"codes": ["ValidEmail.userDto.email","ValidEmail.email","ValidEmail.java.lang.String","ValidEmail"],"arguments": [{"codes": ["userDto.email","email"],"arguments": null,"defaultMessage": "email","code": "email"}],"defaultMessage": "邮箱格式不正确","objectName": "userDto","field": "email","rejectedValue": "zs@local","bindingFailure": false,"code": "ValidEmail"},{"codes": ["ValidPassword.userDto.matchingPassword","ValidPassword.matchingPassword","ValidPassword.java.lang.String","ValidPassword"],"arguments": [{"codes": ["userDto.matchingPassword","matchingPassword"],"arguments": null,"defaultMessage": "matchingPassword","code": "matchingPassword"}],"defaultMessage": "Invalid Password","objectName": "userDto","field": "matchingPassword","rejectedValue": "12345678","bindingFailure": false,"code": "ValidPassword"}],"path": "/authorize/register"
}
密码是否一致验证
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = PasswordMatchesValidator.class)
public @interface PasswordMatches {String message() default "{javax.validation.constraints.NotNull.message}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, UserDto> {@Overridepublic void initialize(PasswordMatches constraintAnnotation) {}@Overridepublic boolean isValid(UserDto userDto, ConstraintValidatorContext constraintValidatorContext) {return Objects.equals(userDto.getPassword(), userDto.getMatchingPassword());}
}
@PasswordMatches(message = "密码不一致")
@Data
public class UserDto implements Serializable {@NotNull@NotBlank@Size(min = 4, max = 50, message = "用户名长度必须在4到50个字符之间")private String username;@NotNull@ValidPasswordprivate String password;@NotNull
// @ValidPasswordprivate String matchingPassword;@NotNull@NotBlank@ValidEmail(message = "邮箱格式不正确")
// @Email
// @Pattern(regexp = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")private String email;@NotNull@NotBlank@Size(min = 4, max = 50, message = "姓名长度必须在4到50个字符之间")private String name;
}
passay 异常的国际化
- 创建一个消息解析器
- 配置验证器使用消息解析器
- 在对应的注解中写消息的键值
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {private final MessageSource messageSource;/*** 配置 Passay 使用 Spring 的 MessageSource* @return*/@Beanpublic MessageResolver messageResolver(){return new SpringMessageResolver(messageSource);}/*** 配置 Java Validation 使用国际化的消息资源* @return*/@Beanpublic LocalValidatorFactoryBean getValidator(){LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();factoryBean.setValidationMessageSource(messageSource);return factoryBean;}
}
@RequiredArgsConstructor
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {private final SpringMessageResolver springMessageResolver;@Overridepublic void initialize(ValidPassword constraintAnnotation) {}@Overridepublic boolean isValid(String password, ConstraintValidatorContext context) {PasswordValidator validator = new PasswordValidator(springMessageResolver,List.of(// 长度规则:8 - 30 位new LengthRule(8, 30),// 至少有一个大写字母new CharacterRule(EnglishCharacterData.UpperCase, 1),// 至少有一个小写字母new CharacterRule(EnglishCharacterData.LowerCase, 1),// 至少有一个数字new CharacterRule(EnglishCharacterData.Digit, 1),// 至少有一个特殊字符new CharacterRule(EnglishCharacterData.Special, 1),// 不允许连续 3 个字母,按字母表顺序// alphabetical is of the form 'abcde', numerical is '34567', qwery is 'asdfg'// the false parameter indicates that wrapped sequences are allowed; e.g. 'xyzabc'new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),// 不允许 3 个连续数字new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false),// 不允许 QWERTY 键盘上的三个连续相邻的按键所代表的字符new IllegalSequenceRule(EnglishSequenceData.USQwerty, 5, false),// 不允许包含空格new WhitespaceRule()));RuleResult validate = validator.validate(new PasswordData(password));if (validate.isValid()) {return true;}context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate(String.join(",", validator.getMessages(validate))).addConstraintViolation();return false;}
}
配置文件
messages.properties
# Passay properties
HISTORY_VIOLATION=Password matches one of {0} previous passwords.
ILLEGAL_WORD=Password contains the dictionary word '{0}'.
ILLEGAL_WORD_REVERSED=Password contains the reversed dictionary word '{0}'.
ILLEGAL_DIGEST_WORD=Password contains a dictionary word.
ILLEGAL_DIGEST_WORD_REVERSED=Password contains a reversed dictionary word.
ILLEGAL_MATCH=Password matches the illegal pattern '{0}'.
ALLOWED_MATCH=Password must match pattern '{0}'.
ILLEGAL_CHAR=Password {1} the illegal character '{0}'.
ALLOWED_CHAR=Password {1} the illegal character '{0}'.
ILLEGAL_QWERTY_SEQUENCE=Password contains the illegal QWERTY sequence '{0}'.
ILLEGAL_ALPHABETICAL_SEQUENCE=Password contains the illegal alphabetical sequence '{0}'.
ILLEGAL_NUMERICAL_SEQUENCE=Password contains the illegal numerical sequence '{0}'.
ILLEGAL_USERNAME=Password {1} the user id '{0}'.
ILLEGAL_USERNAME_REVERSED=Password {1} the user id '{0}' in reverse.
ILLEGAL_WHITESPACE=Password {1} a whitespace character.
ILLEGAL_NUMBER_RANGE=Password {1} the number '{0}'.
ILLEGAL_REPEATED_CHARS=Password contains {2} sequences of {0} or more repeated characters, but only {1} allowed: {3}.
INSUFFICIENT_UPPERCASE=Password must contain {0} or more uppercase characters.
INSUFFICIENT_LOWERCASE=Password must contain {0} or more lowercase characters.
INSUFFICIENT_ALPHABETICAL=Password must contain {0} or more alphabetical characters.
INSUFFICIENT_DIGIT=Password must contain {0} or more digit characters.
INSUFFICIENT_SPECIAL=Password must contain {0} or more special characters.
INSUFFICIENT_CHARACTERISTICS=Password matches {0} of {2} character rules, but {1} are required.
INSUFFICIENT_COMPLEXITY=Password meets {1} complexity rules, but {2} are required.
INSUFFICIENT_COMPLEXITY_RULES=No rules have been configured for a password of length {0}.
SOURCE_VIOLATION=Password cannot be the same as your {0} password.
TOO_LONG=Password must be no more than {1} characters in length.
TOO_SHORT=Password must be {0} or more characters in length.
TOO_MANY_OCCURRENCES=Password contains {1} occurrences of the character '{0}', but at most {2} are allowed.jakarta.validation.constraints.AssertFalse.message = must be false
jakarta.validation.constraints.AssertTrue.message = must be true
jakarta.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'or equal to ' : ''}{value}
jakarta.validation.constraints.DecimalMin.message = must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}
jakarta.validation.constraints.Digits.message = numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
jakarta.validation.constraints.Email.message = must be a well-formed email address
jakarta.validation.constraints.Future.message = must be a future date
jakarta.validation.constraints.FutureOrPresent.message = must be a date in the present or in the future
jakarta.validation.constraints.Max.message = must be less than or equal to {value}
jakarta.validation.constraints.Min.message = must be greater than or equal to {value}
jakarta.validation.constraints.Negative.message = must be less than 0
jakarta.validation.constraints.NegativeOrZero.message = must be less than or equal to 0
jakarta.validation.constraints.NotBlank.message = must not be blank
jakarta.validation.constraints.NotEmpty.message = must not be empty
jakarta.validation.constraints.NotNull.message = must not be null
jakarta.validation.constraints.Null.message = must be null
jakarta.validation.constraints.Past.message = must be a past date
jakarta.validation.constraints.PastOrPresent.message = must be a date in the past or in the present
jakarta.validation.constraints.Pattern.message = must match "{regexp}"
jakarta.validation.constraints.Positive.message = must be greater than 0
jakarta.validation.constraints.PositiveOrZero.message = must be greater than or equal to 0
jakarta.validation.constraints.Size.message = size must be between {min} and {max}org.hibernate.validator.constraints.CreditCardNumber.message = invalid credit card number
org.hibernate.validator.constraints.Currency.message = invalid currency (must be one of {value})
org.hibernate.validator.constraints.EAN.message = invalid {type} barcode
org.hibernate.validator.constraints.Email.message = not a well-formed email address
org.hibernate.validator.constraints.ISBN.message = invalid ISBN
org.hibernate.validator.constraints.Length.message = length must be between {min} and {max}
org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max}
org.hibernate.validator.constraints.LuhnCheck.message = the check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod10Check.message = the check digit for ${validatedValue} is invalid, Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod11Check.message = the check digit for ${validatedValue} is invalid, Modulo 11 checksum failed
org.hibernate.validator.constraints.ModCheck.message = the check digit for ${validatedValue} is invalid, {modType} checksum failed
org.hibernate.validator.constraints.Normalized.message = must be normalized
org.hibernate.validator.constraints.NotBlank.message = may not be empty
org.hibernate.validator.constraints.NotEmpty.message = may not be empty
org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.Range.message = must be between {min} and {max}
org.hibernate.validator.constraints.ScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.UniqueElements.message = must only contain unique elements
org.hibernate.validator.constraints.URL.message = must be a valid URLorg.hibernate.validator.constraints.br.CNPJ.message = invalid Brazilian corporate taxpayer registry number (CNPJ)
org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF)
org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card numberorg.hibernate.validator.constraints.pl.REGON.message = invalid Polish Taxpayer Identification Number (REGON)
org.hibernate.validator.constraints.pl.NIP.message = invalid VAT Identification Number (NIP)
org.hibernate.validator.constraints.pl.PESEL.message = invalid Polish National Identification Number (PESEL)org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'}
org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'}ValidEmail.email=Invalid Email
PasswordMatches.userDto=Passwords do not match
login.page.title=Login
login.page.logout.msg=You have logged out.
login.page.bad-credential=Username or password is wrong
login.page.form.username=Username
login.page.form.password=Password
login.page.form.submit=Login
index.page.menu.sign-out=Sign Out
login.page.form.remember-me=Remember me
messages_zh_CN.properties
# Passay 属性
HISTORY_VIOLATION=密码和您最近用过的 {0} 个密码之一重复。
ILLEGAL_WORD=密码包含了黑名单字典中的词 {0}。
ILLEGAL_WORD_REVERSED=密码包含了保留字典中的词 {0}。
ILLEGAL_DIGEST_WORD=密码包含了字典中的词。
ILLEGAL_DIGEST_WORD_REVERSED=密码包含了保留字典中的词。
ILLEGAL_MATCH=密码匹配了非法结构 {0}。
ALLOWED_MATCH=密码必须要匹配结构 {0}。
ILLEGAL_CHAR=密码 {1} 非法字符 {0}。
ALLOWED_CHAR=密码 {1} 非法字符 {0}。
ILLEGAL_QWERTY_SEQUENCE=密码包含非法的QWERTY序列 {0}。
ILLEGAL_ALPHABETICAL_SEQUENCE=密码包含非法的字母序列 {0}。
ILLEGAL_NUMERICAL_SEQUENCE=密码包含非法的数字序列 {0}。
ILLEGAL_USERNAME=密码 {1} 用户 id {0}。
ILLEGAL_USERNAME_REVERSED=密码 {1} 倒序的用户 id {0}。
ILLEGAL_WHITESPACE=密码 {1} 空格。
ILLEGAL_NUMBER_RANGE=密码 {1} 数字 {0}.
ILLEGAL_REPEATED_CHARS=密码中包含 {2} 序列 {0} 的一个或多个重复字符, 但仅允许 {1} 个: {3}。
INSUFFICIENT_UPPERCASE=密码中必须包含至少 {0} 个大写字母。
INSUFFICIENT_LOWERCASE=密码中必须包含至少 {0} 个小写字母。
INSUFFICIENT_ALPHABETICAL=密码中必须包含至少 {0} 个字母。
INSUFFICIENT_DIGIT=密码中必须包含至少 {0} 个数字。
INSUFFICIENT_SPECIAL=密码中必须包含至少 {0} 个特殊字符。
INSUFFICIENT_CHARACTERISTICS=密码匹配了 {0} of {2} 字符规则, 但只允许 {1} 个。
INSUFFICIENT_COMPLEXITY=密码符合了 {1} 个复杂规则, 但需要符合 {2} 个。
INSUFFICIENT_COMPLEXITY_RULES=对于密码长度 {0},没有配置规则。
SOURCE_VIOLATION=密码不能和之前的 {0} 个历史密码相同。
TOO_LONG=密码长度不能超过 {1} 个字符。
TOO_SHORT=密码长度不能少于 {0} 个字符。
TOO_MANY_OCCURRENCES=密码包含 {1} 个 {0}, 但是至多只允许 {2} 个。jakarta.validation.constraints.AssertFalse.message = 只能为false
jakarta.validation.constraints.AssertTrue.message = 只能为true
jakarta.validation.constraints.DecimalMax.message = 必须小于或等于{value}
jakarta.validation.constraints.DecimalMin.message = 必须大于或等于{value}
jakarta.validation.constraints.Digits.message = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
jakarta.validation.constraints.Email.message = 不是一个合法的电子邮件地址
jakarta.validation.constraints.Future.message = 需要是一个将来的时间
jakarta.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
jakarta.validation.constraints.Max.message = 最大不能超过{value}
jakarta.validation.constraints.Min.message = 最小不能小于{value}
jakarta.validation.constraints.Negative.message = 必须是负数
jakarta.validation.constraints.NegativeOrZero.message = 必须是负数或零
jakarta.validation.constraints.NotBlank.message = 不能为空
jakarta.validation.constraints.NotEmpty.message = 不能为空
jakarta.validation.constraints.NotNull.message = 不能为null
jakarta.validation.constraints.Null.message = 必须为null
jakarta.validation.constraints.Past.message = 需要是一个过去的时间
jakarta.validation.constraints.PastOrPresent.message = 需要是一个过去或现在的时间
jakarta.validation.constraints.Pattern.message = 需要匹配正则表达式"{regexp}"
jakarta.validation.constraints.Positive.message = 必须是正数
jakarta.validation.constraints.PositiveOrZero.message = 必须是正数或零
jakarta.validation.constraints.Size.message = 个数必须在{min}和{max}之间org.hibernate.validator.constraints.CreditCardNumber.message = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}的校验码不合法, {modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message = 不能为空
org.hibernate.validator.constraints.NotEmpty.message = 不能为空
org.hibernate.validator.constraints.ParametersScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.Range.message = 需要在{min}和{max}之间
org.hibernate.validator.constraints.ScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.URL.message = 需要是一个合法的URLorg.hibernate.validator.constraints.time.DurationMax.message = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}ValidEmail.email=非法电子邮件地址
PasswordMatches.userDto=密码输入不一致login.page.title=登录
login.page.logout.msg=您已退出登录
login.page.bad-credential=用户名或密码不正确
login.page.form.username=用户名
login.page.form.password=密码
login.page.form.submit=登录
index.page.menu.sign-out=退出登录
login.page.form.remember-me=记住我