导入同步修改
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package com.yunhai.bschclassmanage.validation;
|
||||
|
||||
/**
|
||||
* 密码校验通用契约:供类级 @PasswordValidation 校验器读取
|
||||
* 密码值与业务主键 id(判断新增/更新),学生/教师等 DTO 实现之。
|
||||
*/
|
||||
public interface PasswordCheckable {
|
||||
|
||||
/** 密码值 */
|
||||
String getPwd();
|
||||
|
||||
/** 业务主键 id:null 或 <=0 视为新增,>0 视为更新 */
|
||||
Integer getId();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yunhai.bschclassmanage.validation;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 类级密码校验(作用于 SchStudentDTO):
|
||||
* - id 为 null 或 <=0(新增场景):密码必填且必须通过全部强度规则;
|
||||
* - id > 0(更新场景):密码为空则跳过;密码非空则同样必须通过全部强度规则。
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Constraint(validatedBy = PasswordValidationValidator.class)
|
||||
@Documented
|
||||
public @interface PasswordValidation {
|
||||
String message() default "密码校验未通过";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.yunhai.bschclassmanage.validation;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 类级校验器:依据 id 判断是否需要校验密码(适用于学生/教师等实现 PasswordCheckable 的 DTO)。
|
||||
* 新增(id == null 或 id <= 0):密码必填且必须满足强度规则;
|
||||
* 更新(id > 0):密码为空跳过,密码非空同样必须满足强度规则。
|
||||
* 各类违规均以独立消息挂在 pwd 属性上返回。
|
||||
*/
|
||||
public class PasswordValidationValidator implements ConstraintValidator<PasswordValidation, PasswordCheckable> {
|
||||
|
||||
private static final int MIN_LEN = 8;
|
||||
private static final int MAX_LEN = 32;
|
||||
/** 连续数字判定的最小位数(升序 1234 / 降序 4321) */
|
||||
private static final int MIN_RUN = 4;
|
||||
|
||||
private static final Pattern COMPOSE =
|
||||
Pattern.compile("^(?=.*[A-Za-z])(?=.*\\d)(?=.*[^A-Za-z0-9]).+$");
|
||||
|
||||
@Override
|
||||
public boolean isValid(PasswordCheckable dto, ConstraintValidatorContext context) {
|
||||
Integer id = dto.getId();
|
||||
boolean isNew = (id == null || id <= 0);
|
||||
|
||||
String pwd = dto.getPwd();
|
||||
// 密码为空:仅新增场景报错;更新场景(id>0)跳过
|
||||
if (pwd == null || pwd.isEmpty()) {
|
||||
if (isNew) {
|
||||
addViolation(context, "新增时密码不允许为空");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 密码非空:新增与更新都必须满足强度规则
|
||||
boolean valid = true;
|
||||
if (pwd.length() < MIN_LEN || pwd.length() > MAX_LEN) {
|
||||
addViolation(context, "密码长度必须为8-32位");
|
||||
valid = false;
|
||||
}
|
||||
if (!COMPOSE.matcher(pwd).matches()) {
|
||||
addViolation(context, "密码必须同时包含字母、数字和符号");
|
||||
valid = false;
|
||||
}
|
||||
if (containsConsecutiveSequence(pwd)) {
|
||||
addViolation(context, "密码不允许包含连续自然数序列(如1234、6789)");
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
private void addViolation(ConstraintValidatorContext context, String msg) {
|
||||
context.disableDefaultConstraintViolation();
|
||||
context.buildConstraintViolationWithTemplate(msg)
|
||||
.addPropertyNode("pwd")
|
||||
.addConstraintViolation();
|
||||
}
|
||||
|
||||
private boolean containsConsecutiveSequence(String s) {
|
||||
int n = s.length();
|
||||
for (int i = 0; i + MIN_RUN <= n; i++) {
|
||||
boolean asc = true; // 升序:1234
|
||||
boolean desc = true; // 降序:4321
|
||||
for (int j = 0; j < MIN_RUN - 1; j++) {
|
||||
int a = s.charAt(i + j);
|
||||
int b = s.charAt(i + j + 1);
|
||||
if (!Character.isDigit(a) || !Character.isDigit(b)) {
|
||||
asc = false;
|
||||
desc = false;
|
||||
break;
|
||||
}
|
||||
if (b - a != 1) {
|
||||
asc = false;
|
||||
}
|
||||
if (a - b != 1) {
|
||||
desc = false;
|
||||
}
|
||||
}
|
||||
if (asc || desc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user