JAVA集成强密码校验

1 : 背景

最近系统需要做用户密码升级,增加强密码校验,密码长度,复杂度等等,所以整理了一份通用的密码复杂度控制代码。
目的:实现一套拿来即用的密码验证功能,可灵活配置,随意挑选规则。需实现可复用性,可迁移性等。

1、引入密码等级概念。
即框定规则,等级数为满足规则的数量,这样等级越高,规则越复杂。
但缺点为不可控,故放弃次方式。
2、仅引入密码开关,密码规则概念。
密码开关做总控,密码规则开关做细分管控,方便随意搭配密码规则。

2 : 代码设计编写

2.1 : 引入规则配置

1、密码检测总控开关
2、密码长度控制开关
3、数字、小写字母、大写字母、特殊符号控制开关
4、键盘横向连续检测开关,如qwer等
5、键盘斜向连续检测开关
6、逻辑位置连续检测开关,如1234、abcd等
7、相邻字符相同检测开关,如aaaa、1111、####等
8、自定义密码检测开关,如固定字符等
由此引入yml配置如下
application.yml

server:
  port: 8080
  servlet:
    context-path: /project

password:
  #密码检测总控开关
  passwordCheckSwitch: true
  #密码长度控制开关
  checkPasswordLength: true
  limitPasswordMinLength: 8
  limitPasswordMaxLength: 20
  #数字、小写字母、大写字母、特殊符号控制开关
  checkContainDigit: true
  checkContainUpperLowerCase: false
  checkContainLowerCase: true
  checkContainUpperCase: true
  checkContainSpecialChar: false
  defaultSpecialChar: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
  #键盘横向连续检测开关,如qwer等
  checkHorizontalKeySequential: false
  limitHorizontalKeyNumber: 3
  #键盘斜向连续检测开关
  checkSlopeKeySequential: false
  limitSlopeKeyNumber: 3
  #逻辑位置连续检测开关,如1234、abcd等
  checkLogicSequential: false
  limitLogicNumber: 3
  #相邻字符相同检测开关,如aaaa、1111、####等
  checkSequentialCharSame: false
  limitSequentialCharNumber: 3
  #自定义密码检测开关,如固定字符等
  checkCustomPasswordArray: true

引入自定义密码文件:password.txt

123456
123456789
1234567890
1234561314
admin1234
test1234
123abc

以上配置引入,在代码中如何注入,可参考文末链接。

对应配置类 PasswordConfigBean.java

package com.bean;

import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;

@Component
public class PasswordConfigBean {
    //开关->开
    public final static String PASS_WORD_STATUS_TRUE = "true";
    //开关->关
    public final static String PASS_WORD_STATUS_FALSE = "false";

    //密码检测开关
    public static volatile String PASS_WORD_CHECK_SWITCH;
    //是否检测密码口令长度标识
    public static volatile String CHECK_PASSWORD_LENGTH;
    //密码最小长度
    public static volatile String LIMIT_PASSWORD_MIN_LENGTH;
    //密码最大长度
    public static volatile String LIMIT_PASSWORD_MAX_LENGTH;
    //是否包含数字
    public static volatile String CHECK_CONTAIN_DIGIT;
    //是否区分大小写
    public static volatile String CHECK_CONTAIN_UPPER_LOWER_CASE;
    //是否包含小写字母
    public static volatile String CHECK_CONTAIN_LOWER_CASE;
    //是否包含大写字母
    public static volatile String CHECK_CONTAIN_UPPER_CASE;
    //是否包含特殊符号
    public static volatile String CHECK_CONTAIN_SPECIAL_CHAR;
    //特殊符号集合
    public static volatile String DEFAULT_SPECIAL_CHAR;
    //是否检测键盘按键横向连续
    public static volatile String CHECK_HORIZONTAL_KEY_SEQUENTIAL;
    //键盘物理位置横向不允许最小的连续个数
    public static volatile String LIMIT_HORIZONTAL_KEY_NUMBER;
    //是否检测键盘按键斜向连续
    public static volatile String CHECK_SLOPE_KEY_SEQUENTIAL;
    //键盘物理位置斜向不允许最小的连续个数
    public static volatile String LIMIT_SLOPE_KEY_NUMBER;
    //是否检测逻辑位置连续
    public static volatile String CHECK_LOGIC_SEQUENTIAL;
    //密码口令中字符在逻辑位置上不允许最小的连续个数
    public static volatile String LIMIT_LOGIC_NUMBER;
    //是否检测连续字符相同
    public static volatile String CHECK_SEQUENTIAL_CHAR_SAME;
    //密码口令中相同字符不允许最小的连续个数
    public static volatile String LIMIT_SEQUENTIAL_CHAR_NUMBER;
    //是否校验自定义密码集合
    public static volatile String CHECK_CUSTOM_PASSWORD_ARRAY;
    //自定义密码集合
    public static volatile List<String> CUSTOM_PASSWORD_ARRAY = new ArrayList<>();
    //键盘横向方向规则
    public static volatile String[] KEYBOARD_HORIZONTAL_ARR = {"01234567890", "qwertyuiop", "asdfghjkl", "zxcvbnm"};
    //键盘斜线方向规则
    public static volatile String[] KEYBOARD_SLOPE_ARR = {
            "1qaz", "2wsx", "3edc", "4rfv", "5tgb", "6yhn", "7ujm", "8ik,", "9ol.", "0p;/", "=[;.", "-pl,", "0okm", "9ijn", "8uhb", "7ygv", "6tfc", "5rdx", "4esz",
            "!QAZ", "@WSX", "#EDC", "$RFV", "%TGB", "^YHN", "&UJM", "*IK<", "(OL>", ")P:?", "+{:>", "_PL<", ")OKM", "(IJN", "*UHB", "&YGV", "^TFC", "%RDX", "$ESZ"
    };

}

对应配置类 PasswordConfigBeanSet.java

package com.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;

@Component
public class PasswordConfigBeanSet {
    @Value("${password.passwordCheckSwitch}")
    public static void setPasswordCheckSwitch(String passwordCheckSwitch) {
        PasswordConfigBean.PASS_WORD_CHECK_SWITCH = passwordCheckSwitch;
    }
    @Value("${password.checkPasswordLength}")
    private static void setCheckPasswordLength(String checkPasswordLength) {
        PasswordConfigBean.CHECK_PASSWORD_LENGTH = checkPasswordLength;
    }
    @Value("${password.limitPasswordMinLength}")
    private void setLimitPasswordMinLength(String limitPasswordMinLength) {
        PasswordConfigBean.LIMIT_PASSWORD_MIN_LENGTH = limitPasswordMinLength;
    }
    @Value("${password.limitPasswordMaxLength}")
    private void setLimitPasswordMaxLength(String limitPasswordMaxLength) {
        PasswordConfigBean.LIMIT_PASSWORD_MAX_LENGTH = limitPasswordMaxLength;
    }
    @Value("${password.checkContainDigit}")
    private void setCheckContainDigit(String checkContainDigit) {
        PasswordConfigBean.CHECK_CONTAIN_DIGIT = checkContainDigit;
    }
    @Value("${password.checkContainUpperLowerCase}")
    private void setCheckContainUpperLowerCase(String checkContainUpperLowerCase) {
        PasswordConfigBean.CHECK_CONTAIN_UPPER_LOWER_CASE = checkContainUpperLowerCase;
    }
    @Value("${password.checkContainLowerCase}")
    private void setCheckContainLowerCase(String checkContainLowerCase) {
        PasswordConfigBean.CHECK_CONTAIN_LOWER_CASE = checkContainLowerCase;
    }
    @Value("${password.checkContainUpperCase}")
    private void setCheckContainUpperCase(String checkContainUpperCase) {
        PasswordConfigBean.CHECK_CONTAIN_UPPER_CASE = checkContainUpperCase;
    }
    @Value("${password.checkContainSpecialChar}")
    private void setCheckContainSpecialChar(String checkContainSpecialChar) {
        PasswordConfigBean.CHECK_CONTAIN_SPECIAL_CHAR = checkContainSpecialChar;
    }
    @Value("${password.defaultSpecialChar}")
    private void setDefaultSpecialChar(String defaultSpecialChar) {
        PasswordConfigBean.DEFAULT_SPECIAL_CHAR = defaultSpecialChar;
    }
    @Value("${password.checkHorizontalKeySequential}")
    private void setCheckHorizontalKeySequential(String checkHorizontalKeySequential) {
        PasswordConfigBean.CHECK_HORIZONTAL_KEY_SEQUENTIAL = checkHorizontalKeySequential;
    }
    @Value("${password.limitHorizontalKeyNumber}")
    private void setLimitHorizontalKeyNumber(String limitHorizontalKeyNumber) {
        PasswordConfigBean.LIMIT_HORIZONTAL_KEY_NUMBER = limitHorizontalKeyNumber;
    }
    @Value("${password.checkSlopeKeySequential}")
    private void setCheckSlopeKeySequential(String checkSlopeKeySequential) {
        PasswordConfigBean.CHECK_SLOPE_KEY_SEQUENTIAL = checkSlopeKeySequential;
    }
    @Value("${password.limitSlopeKeyNumber}")
    private void setLimitSlopeKeyNumber(String limitSlopeKeyNumber) {
        PasswordConfigBean.LIMIT_SLOPE_KEY_NUMBER = limitSlopeKeyNumber;
    }
    @Value("${password.checkLogicSequential}")
    private void setCheckLogicSequential(String checkLogicSequential) {
        PasswordConfigBean.CHECK_LOGIC_SEQUENTIAL = checkLogicSequential;
    }
    @Value("${password.limitLogicNumber}")
    private void setLimitLogicNumber(String limitLogicNumber) {
        PasswordConfigBean.LIMIT_LOGIC_NUMBER = limitLogicNumber;
    }
    @Value("${password.checkSequentialCharSame}")
    private void setCheckSequentialCharSame(String checkSequentialCharSame) {
        PasswordConfigBean.CHECK_SEQUENTIAL_CHAR_SAME = checkSequentialCharSame;
    }
    @Value("${password.limitSequentialCharNumber}")
    private void setLimitSequentialCharNumber(String limitSequentialCharNumber) {
        PasswordConfigBean.LIMIT_SEQUENTIAL_CHAR_NUMBER = limitSequentialCharNumber;
    }
    @Value("${password.checkCustomPasswordArray}")
    private void setCheckCustomPasswordArray(String checkCustomPasswordArray) {
        PasswordConfigBean.CHECK_CUSTOM_PASSWORD_ARRAY = checkCustomPasswordArray;
        if("true".equals(checkCustomPasswordArray)){
            loadCustomPasswordArray();
        }
    }

    //加载自定义密码
    private static void loadCustomPasswordArray() {
        BufferedReader bufferedReader = null;
        ClassPathResource classPathResource = new ClassPathResource("password.txt");
        try (InputStream inputStream = classPathResource.getInputStream();
             InputStreamReader inputStreamReader = new InputStreamReader(inputStream);){
            bufferedReader = new BufferedReader(inputStreamReader);
            String line;
            while(null != (line = bufferedReader.readLine())) {
                PasswordConfigBean.CUSTOM_PASSWORD_ARRAY.add(line);
            }
        } catch (IOException var5) {
            PasswordConfigBean.CUSTOM_PASSWORD_ARRAY.addAll(Arrays.asList(("123456,Qwer,111111,123456789,test1234").split(",")));
        }
    }
}

2.2 : 密码校验工具类

PasswordCheckUtil.java

package com.utils;

import com.bean.PasswordConfigBean;
import java.util.Iterator;

public class PasswordCheckUtil {

    public static boolean checkPassword(String password) {
        if (password == null || "".equals(password)) {
            return false;
        }
        //密码校验总开关
        if (PasswordConfigBean.PASS_WORD_STATUS_FALSE.equals(PasswordConfigBean.PASS_WORD_CHECK_SWITCH)){
            return true;
        }
        //是否在自定义集合中
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CUSTOM_PASSWORD_ARRAY) && checkCustomPasswordArray(password)){
            return false;
        }
        //检测长度
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_PASSWORD_LENGTH) && !checkPasswordLength(password, PasswordConfigBean.LIMIT_PASSWORD_MAX_LENGTH, PasswordConfigBean.LIMIT_PASSWORD_MIN_LENGTH)){
            return false;
        }
        //检测包含数字
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_DIGIT) && !password.matches("(.*[0-9].*)")){
            return false;
        }
        //检测包含字母(区分大小写)
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_UPPER_LOWER_CASE)){
            //检测包含小写字母
            if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_LOWER_CASE) && !password.matches("(.*[a-z].*)")){
                return false;
            }
            //检测包含大写字母
            if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_UPPER_CASE) && !password.matches("(.*[A-Z].*)")){
                return false;
            }
        } else {
            if (!checkContainCase(password)) {
                return false;
            }
        }
        //检测包含特殊符号
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_SPECIAL_CHAR) && !password.matches("(.*[`~!@#$%^&*()+=|{}':;',//[//].<>/?].*)")){
            return false;
        }
        //检测键盘横向连续
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_HORIZONTAL_KEY_SEQUENTIAL) && checkLateralKeyboardSite(password)){
            return false;
        }
        //检测键盘斜向连续
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_SLOPE_KEY_SEQUENTIAL) && checkKeyboardSlantSite(password)){
            return false;
        }
        //检测逻辑位置连续
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_LOGIC_SEQUENTIAL) && checkSequentialChars(password)){
            return false;
        }
        //检测相邻字符是否相同
        if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_SEQUENTIAL_CHAR_SAME) && checkSequentialSameChars(password)){
            return false;
        }
        return true;
    }

    public static boolean checkPasswordForUpdate(String password) {
        if (password == null || "".equals(password)) return false;
        if (!checkPasswordLength(password, "", "8")) return false;
        if (!password.matches("(.*[0-9].*)")) return false;
        if (!password.matches("(.*[a-z].*)")) return false;
        if (!password.matches("(.*[A-Z].*)")) return false;
        return true;
    }

    public static boolean checkCustomPasswordArray(String password) {
        Iterator iterator = PasswordConfigBean.CUSTOM_PASSWORD_ARRAY.iterator();
        while(iterator.hasNext()) {
            if(password.equals(iterator.next())) {
                return true;
            }
        }
        return false;
    }

    public static boolean checkPasswordLength(String password, String max, String min) {
        boolean flag = false;
        if("".equals(max)) {
            if (password.length() >= Integer.parseInt(min)) {
                flag = true;
            }
        } else {
            if (password.length() >= Integer.parseInt(min) && password.length() <= Integer.parseInt(max)) {
                flag = true;
            }
        }
        return flag;
    }

    public static boolean checkContainDigit(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int numberCount = 0;
        for (char c : charArray) {
            if (Character.isDigit(c)) numberCount++;
        }
        if (numberCount >= 1) flag = true;
        return flag;
    }

    public static boolean checkContainCase(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int charCount = 0;
        for (char c : charArray) {
            if (Character.isLetter(c)) charCount++;
        }
        if (charCount >= 1) flag = true;
        return flag;
    }

    public static boolean checkContainLowerCase(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int charCount = 0;
        for (char c : charArray) {
            if (Character.isLowerCase(c)) charCount++;
        }
        if (charCount >= 1) flag = true;
        return flag;
    }

    public static boolean checkContainUpperCase(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int charCount = 0;
        for (char c : charArray) {
            if (Character.isUpperCase(c)) charCount++;
        }
        if (charCount >= 1) flag = true;
        return flag;
    }

    public static boolean checkContainSpecialChar(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int specialCount = 0;
        for (char c : charArray) {
            if (PasswordConfigBean.DEFAULT_SPECIAL_CHAR.indexOf(c) != -1) specialCount++;
        }
        if (specialCount >= 1) flag = true;
        return flag;
    }

    public static boolean checkLateralKeyboardSite(String password) {
        //将所有输入字符转为小写
        String lowerCasePassword = password.toLowerCase();
        //键盘横向规则检测
        int arrLength = PasswordConfigBean.KEYBOARD_HORIZONTAL_ARR.length;
        int limitNumKey = Integer.parseInt(PasswordConfigBean.LIMIT_HORIZONTAL_KEY_NUMBER) ;
        for (int i = 0; i + limitNumKey <= lowerCasePassword.length(); i++) {
            String str = lowerCasePassword.substring(i, i+limitNumKey);
            String distinguishStr = password.substring(i, i+limitNumKey);
            String revOrderStr = null;
            for (int j = 0; j < arrLength; j++) {
                String configStr = PasswordConfigBean.KEYBOARD_HORIZONTAL_ARR[j];
                revOrderStr = new StringBuffer(PasswordConfigBean.KEYBOARD_HORIZONTAL_ARR[j]).reverse().toString();
                //检测包含字母(区分大小写)
                if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_UPPER_LOWER_CASE)) {
                    //考虑 大写键盘匹配的情况
                    String UpperStr = PasswordConfigBean.KEYBOARD_HORIZONTAL_ARR[j].toUpperCase();
                    if(configStr.contains(distinguishStr) || UpperStr.contains(distinguishStr)) {
                        return true;
                    }
                    //考虑逆序输入情况下 连续输入
                    String revUpperStr = new StringBuffer(UpperStr).reverse().toString();
                    if(revOrderStr.contains(distinguishStr) || revUpperStr.contains(distinguishStr)) {
                        return true;
                    }
                } else {
                    if(configStr.contains(str)) return true;
                    //考虑逆序输入情况下 连续输入
                    if(revOrderStr.contains(str)) return true;
                }
            }
        }
        return false;
    }

    public static boolean checkKeyboardSlantSite(String password) {
        //将所有输入字符转为小写
        String lowerCasePassword = password.toLowerCase();
        //键盘斜线方向规则检测
        int arrLength = PasswordConfigBean.KEYBOARD_SLOPE_ARR.length;
        int limitNumKey = Integer.parseInt(PasswordConfigBean.LIMIT_SLOPE_KEY_NUMBER);
        for (int i = 0; i + limitNumKey <= lowerCasePassword.length(); i++) {
            String str = lowerCasePassword.substring(i, i + limitNumKey);
            String distinguishStr = password.substring(i, i + limitNumKey);
            String revOrderStr = null;
            for (int j = 0; j < arrLength; j++) {
                String configStr = PasswordConfigBean.KEYBOARD_SLOPE_ARR[j];
                revOrderStr = new StringBuffer(PasswordConfigBean.KEYBOARD_SLOPE_ARR[j]).reverse().toString();
                //检测包含字母(区分大小写)
                if (PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_UPPER_LOWER_CASE)) {
                    //考虑 大写键盘匹配的情况
                    String UpperStr = PasswordConfigBean.KEYBOARD_SLOPE_ARR[j].toUpperCase();
                    if(configStr.contains(distinguishStr) || UpperStr.contains(distinguishStr)) {
                        return true;
                    }
                    //考虑逆序输入情况下 连续输入
                    String revUpperStr = new StringBuffer(UpperStr).reverse().toString();
                    if(revOrderStr.contains(distinguishStr) || revUpperStr.contains(distinguishStr)) {
                        return true;
                    }
                } else {
                    if(configStr.contains(str)) return true;
                    //考虑逆序输入情况下 连续输入
                    if(revOrderStr.contains(str)) return true;
                }
            }
        }
        return false;
    }

    public static boolean checkSequentialChars(String password) {
        int limitNumber = Integer.parseInt(PasswordConfigBean.LIMIT_LOGIC_NUMBER);
        int normalCount,reversedCount;
        //检测包含字母(区分大小写)
        if (!PasswordConfigBean.PASS_WORD_STATUS_TRUE.equals(PasswordConfigBean.CHECK_CONTAIN_UPPER_LOWER_CASE)) {
            password = password.toLowerCase();
        }
        char[] passwordArray = password.toCharArray();
        for (int i = 0; i + limitNumber <= password.length(); i++) {
            normalCount = 0;
            reversedCount = 0;
            for (int j = 0; j < limitNumber - 1; j++) {
                if (passwordArray[i + j + 1] - passwordArray[i + j] == 1) {
                    normalCount++;
                    if(normalCount == limitNumber - 1){
                        return true;
                    }
                }
                if (passwordArray[i + j] - passwordArray[i + j + 1] == 1) {
                    reversedCount++;
                    if(reversedCount == limitNumber - 1){
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static boolean checkSequentialSameChars(String password) {
        char[] passwordArray = password.toCharArray();
        int limitNumber = Integer.parseInt(PasswordConfigBean.LIMIT_SEQUENTIAL_CHAR_NUMBER);
        int count;
        for (int i = 0; i + limitNumber <= password.length(); i++) {
            count = 0;
            for (int j = 0; j < limitNumber - 1; j++) {
                if(passwordArray[i + j] == passwordArray[i + j + 1]) {
                    count++;
                    if (count == limitNumber - 1){
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

3 : 验证

需要注意的是,配置文件交给Spring管理的,所以验证时,需要将项目启动。

public static void main(String[] args) {
    Boolean check = checkPassword("123");
    System.out.print(check);
}

在这里插入图片描述

4 : 相关链接

Springboot基于BCrypt非对称加密字符串:https://blog.csdn.net/qq_38254635/article/details/129746320
SpringBoot加载自定义yml中的配置参数:https://blog.csdn.net/qq_38254635/article/details/112033193

OK,整理到这吧!

如有不正确之处,还望指正!书写不易,觉得有帮助就点个赞吧!☺☺☺

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

message丶小和尚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值