java密码复杂度匹配规则
在开发中经常需要验证字符串是否匹配纯数字,纯大写,纯小写,大写小写数字特殊字符等任意两种,特别是在密码复杂度匹配时,现添加工具类校验字符串是否满足规则。因为需要设置不匹配的规则,比如至少匹配三种格式,那么久需要设置不能匹配两两组合。
·
java密码复杂度匹配规则
在开发中经常需要验证字符串是否匹配纯数字,纯大写,纯小写,大写小写数字特殊字符等任意两种,特别是在密码复杂度匹配时,现添加工具类校验字符串是否满足规则
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 字符串规则校验器
*
* @version V1.0
* @date 2022/7/18 18:22
*/
@Slf4j
@Component
@NoArgsConstructor
@Getter
@Setter
public class StringValidator extends AbstractValidator {
/**
* 字符串配置信息
*/
private StringConfig config;
/**
* 单个中文汉字<br>
* 参照维基百科汉字Unicode范围(https://zh.wikipedia.org/wiki/%E6%B1%89%E5%AD%97 页面右侧)
*/
private static final String RE_CHINESE = "\u2E80-\u2EFF\u2F00-\u2FDF\u31C0-\u31EF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uD840\uDC00-\uD869\uDEDF\uD869\uDF00-\uD86D\uDF3F\uD86D\uDF40-\uD86E\uDC1F\uD86E\uDC20-\uD873\uDEAF\uD87E\uDC00-\uD87E\uDE1F";
/**
* 默认构造方法
*
* @param config 校验配置
*/
public StringValidator(StringConfig config) {
super(config.getErrMsg());
this.config = config;
}
/**
* 支持的校验参数类型
*
* @return 参数校验枚举
*/
@Override
protected ValidatorCheckTypeEnum supportType() {
return ValidatorCheckTypeEnum.STRING;
}
/**
* 通过配置信息初始化并返回新的校验器
*
* @param config 配置信息
* @return 新校验器
*/
@Override
public ValidatorInterface init(JSONObject config) {
return new StringValidator(config.toJavaObject(StringConfig.class));
}
/**
* 校验是否通过.不通过会抛出ValidatorException
* @param value 参数值
*/
public void validate(String value){
this.validate(value, new SysModelColumnResponse());
}
/**
* 校验是否通过,true:校验通过,false:校验不通过
*
* @param value 当前值
* @param column 上下文环境,列定义信息
*/
@Override
public void validate(String value, SysModelColumnResponse column) {
// 空类型判断
this.checkEmpty(value, this.config.getEmpty(), column);
// 判断字符串长度
this.checkLength(value, column);
// 检测支持的字符类型
this.checkChar(value, column);
}
/**
* 校验字符串支持的字符
*
* @param value 字符串
* @param column 列名称
*/
private void checkChar(String value, SysModelColumnResponse column) {
if (Objects.isNull(value)) {
log.info("对象:{},属性:{},值为空字符串,不校验字符!", column.getDomainObject(), column.getName());
return;
}
StringBuilder sb = new StringBuilder();
sb.append("^");
// 不包含特殊字符
if (StringUtils.isNotBlank(this.config.unsupportSymbol)) {
sb.append("(?![").append(ReUtil.escape(this.config.unsupportSymbol)).append("]+$)");
}
// 至少包含字符个数
if(this.config.leastSupportType != null && this.config.leastSupportType > 1){
List<String> combination = new ArrayList<>(Arrays.asList(this.config.supportType));
if(StringUtils.isNotBlank(this.config.supportSymbol) && !combination.contains(SupportType.SPECIAL_CHAR.name())){
combination.add(SupportType.SPECIAL_CHAR.name());
}
// 字符串组合
Set<Set<String>> strCombination = ListUtil.combination(combination, this.config.leastSupportType-1);
for(Set<String> types: strCombination){
sb.append("(?![");
types.forEach(c->sb.append(this.getSupportTypePattern(c)));
sb.append("]+$)");
}
}
// 包含特定字符
if (ArrayUtils.isNotEmpty(this.config.supportType) || StringUtils.isNotBlank(this.config.supportSymbol)) {
sb.append("[");
// 包含指定类型字符
if (ArrayUtils.isNotEmpty(this.config.supportType)) {
for (String type : this.config.supportType) {
sb.append(this.getSupportTypePattern(type));
}
}
// 包含特殊字符
if (StringUtils.isNotBlank(this.config.supportSymbol)) {
sb.append(ReUtil.escape(this.config.supportSymbol));
}
sb.append("]");
} else {
sb.append(".");
}
// 字符串长度限制
if(Objects.nonNull(this.config.minLength) || Objects.nonNull(this.config.maxLength)){
sb.append("{")
.append(Objects.nonNull(this.config.minLength) ? this.config.minLength :"")
.append(",")
.append(Objects.nonNull(this.config.maxLength) ? this.config.maxLength: "")
.append("}");
}else{
sb.append("+");
}
sb.append("$");
// 判断值是否匹配规则
if (!ReUtil.isMatch(sb.toString(), value)) {
throw this.createValidatorException(column.getName(),
StrUtil.format("对象:{},属性:{},值:{}, 不匹配正则规则:{}", column.getDomainObject(), column.getName(), value, sb));
}
}
/**
* 校验字符串长度
*
* @param value 参数值
* @param column 列名称
*/
private void checkLength(String value, SysModelColumnResponse column) {
if (Objects.isNull(value)) {
log.info("对象:{},属性:{},值为空字符串,不校验字符长度!", column.getDomainObject(), column.getName());
return;
}
// 最大长度
if (Objects.nonNull(this.config.maxLength) && StringUtils.length(value) > this.config.maxLength) {
throw this.createValidatorException(column.getName(),
StrUtil.format("对象:{},属性:{},值:{}, 超过最大长度: {}>{}(max)", column.getDomainObject(), column.getName(), value, StrUtil.length(value), this.config.maxLength));
}
// 最小长度
if (Objects.nonNull(this.config.minLength) && StringUtils.length(value) < this.config.minLength) {
throw this.createValidatorException(column.getName(),
StrUtil.format("对象:{},属性:{},值:{}, 超过最小长度: {}<{}(min)", column.getDomainObject(), column.getName(), value, StrUtil.length(value), this.config.minLength));
}
}
/**
* 判断参数是否为空类型
*
* @param value 参数值
* @param empty 空类型
* @param column 属性列
*/
private void checkEmpty(String value, String empty, SysModelColumnResponse column) {
if (StringUtils.equalsIgnoreCase(NOT_NULL.name(), empty) && Objects.isNull(value)) {
throw this.createValidatorException(column.getName(),
StrUtil.format("对象:{},属性:{},值:{}, 不能为NULL", column.getDomainObject(), column.getName(), value));
}
if (StringUtils.equalsIgnoreCase(NOT_EMPTY.name(), empty) && StringUtils.isEmpty(value)) {
throw this.createValidatorException(column.getName(),
StrUtil.format("对象:{},属性:{},值:{}, 不能为空字符串", column.getDomainObject(), column.getName(), value));
}
if (StringUtils.equalsIgnoreCase(NOT_BLANK.name(), empty) && StringUtils.isBlank(value)) {
throw this.createValidatorException(column.getName(),
StrUtil.format("对象:{},属性:{},值:{}, 不能为空白字符串", column.getDomainObject(), column.getName(), value));
}
}
/**
* 获取支持类型的正则表达式
* @param supportType 支持类型
* @return 正则表达式
*/
private String getSupportTypePattern(String supportType){
if(SupportType.SPECIAL_CHAR.name().equalsIgnoreCase(supportType)){
return ReUtil.escape(this.config.supportSymbol);
}else {
SupportType parse = SupportType.parse(supportType);
if (parse != null) {
return parse.getPattern();
}
}
return null;
}
@Data
public static class StringConfig {
/**
* 最大长度
*/
private Integer maxLength;
/**
* 最小长度
*/
private Integer minLength;
/**
* 支持的字符
*/
private String supportSymbol;
/**
* 不支持的字符
*/
private String unsupportSymbol;
/**
* 支持的类型
*/
private String[] supportType;
/**
* 至少包含支持类型的个数,例如:密码至少需要包含大写、小写、数字 任意2种
*/
private Integer leastSupportType;
/**
* 空类型
*/
private String empty;
/**
* 提示消息
*/
private String errMsg;
}
/**
* 字符串空类型
*/
enum EmptyType {
NOT_BLANK, NOT_EMPTY, NOT_NULL;
}
/**
* 支持的字符类型
*/
@Getter
@AllArgsConstructor
enum SupportType {
// 中文
CHINESE(RE_CHINESE),
// 字母
GENERAL("a-zA-Z"),
// 数字
NUMBER("0-9"),
// 大写
UPPER_CASE("A-Z"),
// 小写
LOWER_CASE("a-z"),
// 特殊字符, 会使用supportSymbol指定的字符
SPECIAL_CHAR(""),
;
/**
* 正则表达式
*/
private final String pattern;
/**
* 支持的类型
* @param name 名称
* @return 类型
*/
public static SupportType parse(String name){
for (SupportType type: SupportType.values()){
if(StringUtils.equalsIgnoreCase(name, type.name())){
return type;
}
}
return null;
}
}
public static void main(String[] args) {
StringConfig conf = new StringConfig();
conf.setMaxLength(20);
conf.setMinLength(4);
conf.setSupportSymbol("!@#$%^&*()_+{}[]");
conf.setUnsupportSymbol(".");
conf.setSupportType(new String[]{"NUMBER","UPPER_CASE","LOWER_CASE"});
conf.setLeastSupportType(2);
conf.setEmpty("NOT_BLANK");
conf.setErrMsg("密码校验错误");
StringValidator stringValidator = new StringValidator(conf);
String[] passwords = {
"123456",
"123456.",
"abcdefg",
"abcdefg.",
"ABDCEDG",
"ABDCEDG.",
"!@#$%^&*()",
"abcdEFG",
"abcdEFG.",
"abc1233",
"abc1233.",
"ABC1234",
"123!@#$%^&*()",
"ABC!@#$%^&*()",
"!@#$%^&*()abc",
"ab123Ab",
"abAb!@#$",
"123AB!@#$",
"ab123!@#$",
"ab123Ab!@#$",
"ab123Ab@223^&*",
"委屈@123Abnd&*("
};
for (String password: passwords){
try {
stringValidator.validate(password);
log.info("校验:-------------------------{} 成功", password);
} catch (ValidatorException e) {
log.error("参数:{},{}:{}",password,e.getMessage(), e.getErrorMessage());
}
}
}
}
执行结果:
16:37:21.434 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:123456,null: 密码校验错误:对象:null,属性:null,值:123456, 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.439 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:123456.,null: 密码校验错误:对象:null,属性:null,值:123456., 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.439 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:abcdefg,null: 密码校验错误:对象:null,属性:null,值:abcdefg, 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.439 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:abcdefg.,null: 密码校验错误:对象:null,属性:null,值:abcdefg., 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.439 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:ABDCEDG,null: 密码校验错误:对象:null,属性:null,值:ABDCEDG, 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.439 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:ABDCEDG.,null: 密码校验错误:对象:null,属性:null,值:ABDCEDG., 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.440 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:!@#$%^&*(),null: 密码校验错误:对象:null,属性:null,值:!@#$%^&*(), 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.440 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------abcdEFG 成功
16:37:21.440 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:abcdEFG.,null: 密码校验错误:对象:null,属性:null,值:abcdEFG., 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.440 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------abc1233 成功
16:37:21.440 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:abc1233.,null: 密码校验错误:对象:null,属性:null,值:abc1233., 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
16:37:21.441 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------ABC1234 成功
16:37:21.441 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------123!@#$%^&*() 成功
16:37:21.441 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------ABC!@#$%^&*() 成功
16:37:21.441 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------!@#$%^&*()abc 成功
16:37:21.441 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------ab123Ab 成功
16:37:21.441 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------abAb!@#$ 成功
16:37:21.442 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------123AB!@#$ 成功
16:37:21.442 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------ab123!@#$ 成功
16:37:21.442 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------ab123Ab!@#$ 成功
16:37:21.442 [main] INFO com.aimilin.common.model.validator.StringValidator - 校验:-------------------------ab123Ab@223^&* 成功
16:37:21.442 [main] ERROR com.aimilin.common.model.validator.StringValidator - 参数:委屈@123Abnd&*(,null: 密码校验错误:对象:null,属性:null,值:委屈@123Abnd&*(, 不匹配正则规则:^(?![\.]+$)(?![0-9]+$)(?![A-Z]+$)(?![!@#\$%\^&\*\(\)_\+\{\}\[\]]+$)(?![a-z]+$)[0-9A-Za-z!@#\$%\^&\*\(\)_\+\{\}\[\]]{4,20}$
因为需要设置不匹配的规则,比如至少匹配三种格式,那么久需要设置不能匹配两两组合。
列表指定元素个数求组合:
/**
* 列表工具类
*
* @author liujunguang1
* @version V1.0
* @date 2022/9/8 15:07
*/
public class ListUtil {
/**
* 列表中指定元素组合
* @param remainEle 元素
* @param fetchCount 指定元素组合
* @return 结果
*/
public static <T> Set<Set<T>> combination(List<T> remainEle, int fetchCount) {
return combination(remainEle, new ArrayList<>(), fetchCount);
}
/**
* 列表 组合
* @param remainEle 元素列表
* @param tempCollection 临时存储对象
* @param fetchCount 组合元素个数
* @return 组合结果
*/
private static <T> Set<Set<T>> combination(List<T> remainEle, List<T> tempCollection, int fetchCount) {
if (CollectionUtils.isEmpty(remainEle)) {
return new HashSet<>();
}
if (fetchCount == 1) {
Set<Set<T>> eligibleCollections = new HashSet<>();
// 在只差一个元素的情况下,遍历剩余元素为每个临时集合生成多个满足条件的集合
for (T ele : remainEle) {
Set<T> collection = new HashSet<>(tempCollection);
collection.add(ele);
eligibleCollections.add(collection);
}
return eligibleCollections;
}
fetchCount--;
Set<Set<T>> result = new HashSet<>();
// 差多个元素时,从剩余元素中取出一个,产生多个临时集合,还需要取 count-- 个元素。
for (int i = 0; i < remainEle.size(); i++) {
List<T> collection = new ArrayList<>(tempCollection);
List<T> tempRemain = new ArrayList<>(remainEle);
collection.add(tempRemain.remove(i));
result.addAll(combination(tempRemain, collection, fetchCount));
}
return result;
}
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)