导入同步修改

This commit is contained in:
2026-08-24 18:28:37 +08:00
parent 431a1415fe
commit 505a47d048
3 changed files with 124 additions and 0 deletions
@@ -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 &lt;= 0):密码必填且必须满足强度规则;
* 更新(id &gt; 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;
}
}