最近系统需要做用户密码升级,增加强密码校验,密码长度,复杂度等等,所以整理了一份通用的密码复杂度控制代码。
目的:实现一套拿来即用的密码验证功能,可灵活配置,随意挑选规则。需实现可复用性,可迁移性等。
1、引入密码等级概念。
即框定规则,等级数为满足规则的数量,这样等级越高,规则越复杂。
但缺点为不可控,故放弃次方式。
2、仅引入密码开关,密码规则概念。
密码开关做总控,密码规则开关做细分管控,方便随意搭配密码规则。
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(",")));
}
}
}
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;
}
}
需要注意的是,配置文件交给Spring管理的,所以验证时,需要将项目启动。
public static void main(String[] args) {
Boolean check = checkPassword("123");
System.out.print(check);
}
Springboot基于BCrypt非对称加密字符串:https://blog.csdn.net/qq_38254635/article/details/129746320
SpringBoot加载自定义yml中的配置参数:https://blog.csdn.net/qq_38254635/article/details/112033193
OK,整理到这吧!
如有不正确之处,还望指正!书写不易,觉得有帮助就点个赞吧!☺☺☺