简体   繁体   中英

Java using regular expressions

I am working with resetting passwords. I need a way in java using regular expressions to reset the password so it cannot contain 4 consecutive characters from the previous password.

I don't think that regular expression is an apropriate tool for this task.

Actually, I can't see any other solution then a loop which will try to find a substring of 4 common characters.

Regular expressions isn't solution to every world problem. You code would be much simpler and clearer if you implemented this as normal loop with 'indexOf' check.

edit
I'm assuming you plan to use it in some application. If you ask just out of curiosity, then I don't know :)

You'll have to build that dynamically. Say you have a previous password of 'tiddlywinks'. That would mean the following are not allowed:

tidd iddl ddly dlyw lywi ywin inks

Just so happens it's nice and ends on a 4 character boundary. Didn't plan that one. Anyway. Walk through your previous password like so:

public Pattern toValidationExpression( String previousPassword ) {
   StringBuilder builder = new StringBuilder();
   for( int i = 0; i < previousPassword.length()-4; i++ ) {
      if( i > 0 ) builder.append( "|" );
      builder.append( "(" );
      builder.append( previousPassword.substring(i, Math.min( i+4, previousPassword.length() );
      builder.append( ")" );
   }
   return Pattern.compile( builder.toString() );
}

With some reasonable restrictions, this can in fact be done quite easily with regex.

Assuming that \\n is an illegal part of the password (VERY reasonable restriction), then the following code will do the check quite readably (if you know how to read regex):

static boolean legalChange(String before, String after) {
    String joined = String.format("%s\n%s", before, after);
    return !joined.matches(".*(.{4}).*\n.*\\1.*");
}

Here's a test harness ( see also on ideone.com ):

    String[][] tests = {
        { "abcdef", "ghijklm" },                // true
        { "abcdef", "xbcdeyz" },                // false
        { "abcdef", "fedbcda" },                // true
        { "xyz", "" },                          // true
        { "0123456789", "abc123def456ghi" },    // true
        { "0123456789", "abc123456ghi" },       // false
    };
    for (String[] test : tests) {
        System.out.println(legalChange(test[0], test[1]));
    }

Note that this regex only checks for the "no common substring of length 4" rule. Additional password regex rules must also be in place.


How it works

Essentially we combine the two strings into one, delimited by \\n (which again is an illegal character to have in usernames anyway).

This regex, then, will match an ILLEGAL change:

   before | after              i.e. before = .* (.{4}) .*
          |                         after  = .*   \1   .*
.*(.{4}).*\n.*\1.*
  \____/
    1

That is, we match and capture (.{4}) from before into group 1, and use backreference \\1 to see if it can be matched in after . We put .* around those two to allow the occurrence to happen anywhere. The regex engine will do all the necessary backtracking on the .* to see if this pattern can match.

Since that pattern matches an ILLEGAL change, we negate the result of matches with the boolean complement operator ! .

This is not the most efficient solution for the problem, but assuming that passwords are of reasonable length, speed will not be an issue.

References

So, lets assume you are using char[] instead of String to store old/new passwords to be safe. 安全。 Security experts have better tools...encryption/hashing/etc... for password management.

So, here goes:

/**
 * Compare old to new password to a repeating 4char sequence.
 * -> require passNew to be >= 4char sequence, else return false.
 * -> require passOld to be >= 4char sequence, else return true.
 * -> utilize shifting long to handle full 16Bit char range.
 * -> does not support char beyond 16Bit char range... e.g. codepoint > 2^16.
 * -> codepoint > 2^16 support could be done with XOR 4 codepoints with 8bit shifts
 * @return success true if passNew >= 4char sequence,
 *    passOld lessthan 4char sequence or
 *    normal... 4char sequence not found.
 */
public final boolean validatePassword(char[] passOld, char[] passNew) {
    if (passNew.length lessthan 4) {
        return (false);
    } else if (passOld lessthan 4) {
        return (true);
    }
    long patOld = ((passOld[0] & 0x0ffL) leftshift 32) |
        ((passOld[1] & 0x0ffL) leftshift 16) |
        (passOld[2] & 0x0ffL);
    long patNewOrig = ((passNew[0] & 0x0ffL) leftshift 32) |
        ((passNew[1] & 0x0ffL) leftshift 16) |
        (passNew[2] & 0x0ffL);
    long patNew;
    int offOld = 2, lenOld = passOld.length
    int offNew = 2, lenNew = passNew.length;
    while (++offOld lessthan lenOld) {
        patOld = (patOld leftshift 16) | (passOld[offOld] & 0x0ffL);
        patNew = patNewOrig;
        offNew = 2;
        while (++offNew lessthan lenNew) {
            patNew = (patNew leftshift 16) | (passNew[offNew] & 0x0ffL);
            if (patOld == patNew) {
                return (false);
            }
        }
    }
    return (true);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM