1

Need to validate passwords with next restrictions:

  • at least 1 digit,
  • at least 1 Latin lower case character,
  • at least 1 Latin upper case character,
  • at least 1 special character (not a digit, Latin lower or upper case character).

So, I wrote a regex: (?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^\\da-zA-Z]).

However, 12345678@Cd doesn't match this regex and I can't get why.

P.S. Maybe it's a noobie question but regular expressions were always my Achilles' heel.

Denis Sologub
  • 7,277
  • 11
  • 56
  • 123
  • 6
    Worth noting, it is going to be both easier and more computationally efficient to solve this with a simple loop. Also, requiring special characters doesn't improve password strength much, as everyone just uses @ or similar. You are better off imposing a high min length. – Nick Bailey Jun 19 '22 at 01:20
  • 2
    Works for me: https://regex101.com/r/TSnABc/1 – Nick Jun 19 '22 at 01:20
  • 2
    Java regex expects to match the entire string. – phatfingers Jun 19 '22 at 01:21
  • 4
    I'm going to amplify Nick's comment and say this is an unmitigated maintenance nightmare, even if you get it to work. Please document whatever it does so that someone else has a chance of fixing it later if needed. – markspace Jun 19 '22 at 01:24
  • @Nick, thank you, your expression really works. I didn't add `.*` at the end, seems I got how it works. Thank you one more time for this sample. – Denis Sologub Jun 19 '22 at 01:25
  • @markspace too many Nick's, can you edit your comment to clarify it's about Nick Bailey's? – Nick Jun 19 '22 at 01:27
  • @NickBailey, I understand what you mean, there is a length restriction too, it is checked before this regular expression. – Denis Sologub Jun 19 '22 at 01:29
  • Sorry, past the editing threshold now, I meant the first Nick, Bailey. – markspace Jun 19 '22 at 01:42
  • 1
    You could use a look-up table as in this answer: https://stackoverflow.com/questions/57682576/most-efficient-way-to-find-if-a-string-is-mixedcase/57683560#57683560 – m69's been on strike for years Jun 19 '22 at 03:13
  • @m69isdisappointedinSE Hmm, I didn’t even hear about look-up tables before… Thank you, I’ll learn more about it – Denis Sologub Jun 19 '22 at 03:38
  • Please provide more code. Are you using `matches()` and have [read this](https://stackoverflow.com/a/8923446/5527985)? Your current regex matches one or many *zero-width positions*. It can never match the full string. [Attach `.+` at the end (demo)](https://tio.run/##dY9LS8UwEIX3/RVDV6nSgG@hXFSKywtCceN9QGxjTU3akEwvPri/vY5punSR5OR8M5OTThxE3jUf06SMHRxCRwYfUWnuZCs/@Vpg/S5dkfzDnwSidH2RJHZ81aqGWgvvYS1UDz8JQHQ9CqTjMKgGDDFWoVN9u9mBcK3PQilA2GZCHQ5WkJ6dX1xeXd/c3pdNWgQeXwRLOGpeD8YqLVnK7lb8ZLttsiA2Iv/eRfmQvyxyTwVEgsNP02yeG38KhuZabuYboxjEQ4F6Yyb6nmVLZgr85VEaPozILUVH3bO0ei7Lx6paZh@Tv3Wcpl8) or use `find()` – bobble bubble Jun 19 '22 at 11:07

1 Answers1

1

Since the criteria could change I would simply use a loop as follows. I would find this easier to maintain. I also added a test for length.

String passwd = "ABC@Fab2de@";
System.out.println(isValid(passwd));

prints

true
  • The idea here is simple. Just latch the boolean for each test to true when the test is satisfied.
  • Then return the booleans ANDed together
public static boolean isValid(String password) {
    boolean digit = false;
    boolean lcase = false;
    boolean ucase = false;
    boolean special = false;
    String specialCharacters = "$@!_&";

    if (password.length() < 8) {
        return false;
    }
    
    for (char ch : password.toCharArray()) {
        digit = digit || Character.isDigit(ch);
        ucase = ucase || Character.isUpperCase(ch);
        lcase = lcase || Character.isLowerCase(ch);
        special = special || specialCharacters.indexOf(ch) >= 0;
    }
    return digit && lcase && ucase && special;
}
WJS
  • 36,363
  • 4
  • 24
  • 39