我正在尝试创建一个java来用regex检查密码的强度。密码必须通过4个条件中的3个:
- 小写
- 大写
- 包含数字
- 具有特殊字符
代码如下所示:
import java.util.*;
public class StringPasswordStrength {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter password: ");
String password = input.nextLine();
boolean test = checkStrength(password);
if (test) {
System.out.print("OK");
}
else {
System.out.print("Not strong enough!");
}
}
public static boolean checkStrength(String password) {
if (password.matches("^(?=.*[a-zA-Z][0-9][!@#$%^&*])(?=.{8,})")){
return true;
}
else {
return false;
}
}
}
但是,当密码为Passw0rd
时,它不接受。如何更改正则表达式中程序将接受Passw0rd
的条件,因为它传递了4个条件中的3个:大写、小写和数字?
我建议为此避免使用潜在的隐式正则表达式,而是提供更易于阅读、理解和维护的内容(尽管更详细(。
像这样的事情(当然,这取决于你的情况(。例如,长度测试应该是强制性的:
public boolean isValid(String password) {
// password must contain 3 out of 4 of lowercase, uppercase, digits,
// and others (punctuation, symbols, spaces, etc.).
if (password == null) {
return false;
}
if (password.length() < 8) {
return false;
}
char[] chars = password.toCharArray();
int lowers = 0;
int uppers = 0;
int digits = 0;
int others = 0;
for (Character c : chars) {
if (Character.isLowerCase(c)) {
lowers = 1;
} else if (Character.isUpperCase(c)) {
uppers = 1;
} else if (Character.isDigit(c)) {
digits = 1;
} else {
others = 1;
}
}
// at least 3 out of 4 tests must pass:
return (lowers + uppers + digits + others >= 3);
}
我知道这不是对你问题的直接回答,因此可能无法满足你的需求。
对于Java中的密码处理,我也有意避免讨论char[]
与String
——例如,请参阅本文。
编辑:删除了与密码长度有关的措辞,并更改了相关代码以反映问题。
您可以定义一组规则(regex(,计算给定密码符合的数量,并与所需的最小值进行比较。一种可能的实现方式是:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* Patterns to be tested in your passwords. If you want some of them
* mandatory, you can define them in a "mandatoryPatterns" list and
* check those ones always.
*/
static List<String> patterns = Arrays.asList(
".*[A-Z]+.*",
".*[a-z]+.*"
);
/** Number of required patterns. */
static long requiredPatterns = 1;
/** This functions counts the number of patterns that a password matches. */
static long passwordStrength(String password) {
return patterns.stream().filter(password::matches).count();
}
static boolean checkStrength(String password) {
return passwordStrength(password) >= requiredPatterns;
}
Stream.of("", "foo", "BAR", "FooBar").forEach(pass -> {
System.out.println(pass);
System.out.println(passwordStrength(pass));
System.out.println(checkStrength(pass));
});
您的问题已经被另一个用户指出,并提供了解决方案。这是另一种解决方案。
有4个Pattern
对象,每个对象对应一个需求
Pattern uppercase = Pattern.compile("[A-Z]");
Pattern number = Pattern.compile("\d+");
Pattern symbol = Pattern.compile("[+&$%!@]");
Pattern other = ...;
每次调用正则表达式时,字符串#都匹配"编译"正则表达式,这可能很耗时。通过使用Pattern对象,您将使用已编译的regex模式
将要求添加到列表中
List<Pattern> requirements = Arrays.asList(uppercase, number, symbol, other);
循环浏览需求列表。对于每个要求,请检查密码是否符合要求。如果元素满足,则增加一个计数器,用于跟踪已经满足的需求数量。
如果需求等于3(或大于3(,则返回true
。如果循环正常退出,则意味着3个要求未得到满足;如果循环正常退出,则返回false
。
public boolean isStrong(String password) {
int requirementsMet = 0;
for(Pattern req : requirements) {
if(req.matcher(password).matches())
requirementsMet++;
if(requirementsMet >= 3)
return true;
}
return false;
}
我假设四个要求如下,其中三个必须满足。字符串必须包含:
- 一封信
- 一个数字
- 字符串"!@#$%^&*"中的字符
- 至少8个字符
使用正则表达式是确定密码是否满足四个要求中的三个要求的最佳方法吗?这可能是一个有效的问题,但这不是被问到的问题,也不是我试图回答的问题。OP可能只是好奇:这个问题可以用正则表达式来解决吗?此外,即使有更好的方法来解决这个问题,对所提出的特定问题的回答也有教育价值。
我不熟悉Java,但我可以建议使用Ruby语法的正则表达式。不熟悉Ruby的读者应该能够理解这个表达式,并且将它翻译成Java应该很简单。(如果读者能够进行翻译,我将很高兴看到对这个答案的编辑,它在最后提供了Java等价物。(
r = /
((?=.*[a-z])) # match a lowercase letter in the string in
# a positive lookahead in cap grp 1, 0 times
((?=.*d)) # match a digit in the string in a positive
# lookahead in cap grp 2, 0 times
((?=.*[!@#$%^&*])) # match a special character in in the string
# in a positive lookahead in cap grp 3, 0 times
(.{8,}) # match at least 8 characters in cap grp 4, 0 times
g<1>g<2>g<3> # match conditions 1, 2 and 3
| # or
g<1>g<2>g<4> # match conditions 1, 2 and 4
| # or
g<1>g<3>g<4> # match conditions 1, 3 and 4
| # or
g<2>g<3>g<4> # match conditions 2, 3 and 4
/xi # case indiff & free-spacing regex def modes
例如,g{2}
被捕获组2中包含的子表达式取代((?=.*d)
(。前四行分别包含捕获组中的一个表达式,捕获组重复零次。这只是一个用于定义捕获组中的子表达式以供以后检索的设备。
让我们测试一些字符串。
"Passw0rd".match? r #=> true (a letter, digit and >= 8)
"ab2c#45d".match? r #=> true (all 4 conditions satisfied)
"ab2c#5d".match? r #=> true (a letter, digit and special char)
"ab2c345d".match? r #=> true (a letter, digit and >= 8)
"ab#c?def".match? r #=> true (a letter, special char and >= 8)
"21#6?512".match? r #=> true (a digit, special char and >= 8)
"ab26c4".match? r #=> false (only letter and digit)
"a$b#c".match? r #=> false (only letter and special char)
"abc ef h".match? r #=> false (only letter and >= 8)
"12 45 78".match? r #=> false (only digit and >=8)
"########".match? r #=> false (only special char and >= 8)
"".match r #=> false (no condition matched)
为了使用命名的捕获组,((?=.*[a-z]))
将被替换为
(?<letter>(?=.*[a-z]))
CCD_ 11将被类似的东西所取代
g<letter>g<digit>g<spec_char>
要回答您的问题,您提供的顺序
第一个:[a-zA-Z]
字符
第二:[0-9]
数字
第三:[!@#$%^&*]
Sp.Chars
必须在此序列中出现文字。Abcd1234@
将通过,但Abcd1234@A
不会通过,因为A
在[!@#$%^&*]
之后再次出现
一个积极的前瞻必须只包括这个序列。如果您之前提供了任何特殊的字符,它将不会被验证,类似地,在您的情况下,数字后面的字符是不期望的。
对每个组合使用正向前瞻,对每个使用组
试试这个(这是我在几次查找后的工作(:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%&*_])(?!.*[`~^=+/?<>():;-])(?=S+$).{8,20}$
在这种情况下:任何提供的字符或文字都可以出现在任何地方。
(?=.*[0-9])
(?=.*[a-z])
(?=.*[A-Z])
(?=.*[!@#$%&*_])
(?!.*[`~^=+/?<>():;-])
(?=S+$)