Reputation: 1271
I am using the following regular expression. (?=.+[a-z])(?=.+[A-Z])(?=.+[^a-zA-Z]).{8,}
my goal is to have a password that has 3 of the 4 properties below
upper case character, lower case character, number, special character
I am using http://rubular.com/r/i26nwsTcaU and http://regexlib.com/RETester.aspx to test the expression with the following inputs
P@55w0rd
password1P
Password1
paSSw0rd
all of these should pass but only the second and fourth are passing at http://rubular.com/r/i26nwsTcaU and all of them pass at http://regexlib.com/RETester.aspx.
I also have the following code that I am using to validate
private void doValidate(String inputStr,String regex) {
Pattern pattern = Pattern.compile(regex);
if(!pattern.matcher(inputStr).matches()){
String errMessage = "";
throw new UniquenessConstraintViolatedException(errMessage);
}
}
this code fails to validate "Password1" which should pass. as far as the expression goes I understand it like this
must have lower (?=.+[a-z])
must have upper (?=.+[A-Z])
must have non alpha (?=.+[^a-zA-Z])
must be eight characters long .{8,}
can anyone tell me what it is I'm doing wrong.
Thanks in advance.
Upvotes: 0
Views: 135
Reputation: 36229
I wouldn't use such a regex.
If you like to tell the client what is wrong with his password, you have investigate the password again. In real world environments you might want to support characters from foreign locales.
import java.util.*;
/**
Pwtest
@author Stefan Wagner
@date Fr 11. Mai 20:55:38 CEST 2012
*/
public class Pwtest
{
public int boolcount (boolean [] arr) {
int sum = 0;
for (boolean b : arr)
if (b)
++sum;
return sum;
}
public boolean [] rulesMatch (String [] groups, String password) {
int idx = 0;
boolean [] matches = new boolean [groups.length];
for (String g: groups) {
matches[idx] = (password.matches (".*" + g + ".*"));
++idx;
}
return matches;
}
public Pwtest ()
{
String [] groups = new String [] {"[a-z]", "[A-Z]", "[0-9]", "[^a-zA-Z0-9]"};
String [] pwl = new String [] {"P@55w0rd", "password1P", "Password1", "paSSw0rd", "onlylower", "ONLYUPPER", "1234", ",:?!"};
List <boolean[]> lii = new ArrayList <boolean[]> ();
for (String password: pwl) {
lii.add (rulesMatch (groups, password));
}
for (int i = 0 ; i < lii.size (); ++i) {
boolean [] matches = lii.get (i);
String pw = pwl[i];
if (boolcount (matches) < 3) {
System.out.print ("Password:\t" + pw + " violates rule (s): ");
int idx = 0;
for (boolean b: matches) {
if (! b)
System.out.print (groups[idx] + " ");
++idx;
}
System.out.println ();
}
else System.out.println ("Password:\t" + pw + " fine ");
}
}
public static void main (String args[])
{
new Pwtest ();
}
}
Output:
Password: P@55w0rd fine
Password: password1P fine
Password: Password1 fine
Password: paSSw0rd fine
Password: onlylower violates rule (s): [A-Z] [0-9] [^a-zA-Z0-9]
Password: ONLYUPPER violates rule (s): [a-z] [0-9] [^a-zA-Z0-9]
Password: 1234 violates rule (s): [a-z] [A-Z] [^a-zA-Z0-9]
Password: ,:?! violates rule (s): [a-z] [A-Z] [0-9]
Password: Übergrößen345 fine
Password: 345ÄÜö violates rule (s): [a-z] [A-Z]
Upvotes: 0
Reputation: 10020
Essentially, the .+
subexpressions are to blame, they should be .*
. Otherwise, the lookahead part looks for lower case, upper case or non-alpha but a character of each corresponding type does not count if it is the first one in string. So, you are validating not the password, but the password with first char truncated. While @Cfreak is not right, he is close - what you are doing would not be possible with normal regex and you would have to use what he suggests. With the lookahead groups - (?=)
- it is possible to do what you need. Still, personally I would rather code it like @Cfreak suggests - it is more readable and your intentions are clearer from the code. Complex regular expressions tend to be hard to write but close to impossible to read, debug, or improve after some time.
Upvotes: 1
Reputation: 28687
As I said in a comment, position-0 capital letters are being ignored.
Here's a regex to which all four passwords match.
(?=.+\\d)(?=.+[a-z])(?=\\w*[A-Z]).{8,}
Upvotes: 0
Reputation: 19309
Your regex right now says you must have 1 or more lowercase characters, followed by 1 or more upper case characters followed by 1 or more upper or lowercase characters, followed by 8 characters or more.
Regex can't do AND unless you specify where a particular character appears. You basically need to split each part of your regex into it's own regex and check each one. You can check the length with whatever string length method Java has (sorry i'm not a java dev so I don't know what it is off my head).
Pseudo code:
if( regexForLower && regexForUpper && regexForSpecial && string.length == 8 ) {
// pass
}
Upvotes: 0