依赖
<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,})$";
@Override
public void initialize(final ValidEmail constraintAnnotation) {}
@Override
public 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> {
@Override
public void initialize(ValidPassword constraintAnnotation) {
}
@Override
public 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
@ValidPassword
private String password;
@NotNull
@ValidPassword
private 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> {
@Override
public void initialize(PasswordMatches constraintAnnotation) {
}
@Override
public 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
@ValidPassword
private String password;
@NotNull
// @ValidPassword
private 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
*/
@Bean
public MessageResolver messageResolver(){
return new SpringMessageResolver(messageSource);
}
/**
* 配置 Java Validation 使用国际化的消息资源
* @return
*/
@Bean
public LocalValidatorFactoryBean getValidator(){
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setValidationMessageSource(messageSource);
return factoryBean;
}
}
@RequiredArgsConstructor
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {
private final SpringMessageResolver springMessageResolver;
@Override
public void initialize(ValidPassword constraintAnnotation) {
}
@Override
public 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 URL
org.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 number
org.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 = 需要是一个合法的URL
org.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=记住我