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;
    }
}

正则表达式:密码中至少包含大写字母、小写字母、数字、特殊字符等字符中的2种或3种

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐