Во всех приведенных ранее ответах используется один и тот же (правильный) метод для использования отдельного просмотра вперед для каждого требования. Но они содержат пару недостатков и потенциально серьезную ошибку, в зависимости от серверной части, которая действительно будет использовать пароль.
Я начну с регулярного выражения из принятого ответа:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$
Прежде всего, поскольку Java поддерживает, \A
и \z
я предпочитаю использовать их, чтобы убедиться, что вся строка проверена независимо от Pattern.MULTILINE
. Это не влияет на производительность, но позволяет избежать ошибок при повторном использовании регулярных выражений.
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z
Проверить, не содержит ли пароль пробелов и проверить его минимальную длину, можно за один проход, используя все сразу, указав квантификатор переменной {8,}
в сокращении \S
, ограничивающем допустимые символы:
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z
Если предоставленный пароль действительно содержит пробел, все проверки будут выполнены только для того, чтобы окончательная проверка не прошла. Этого можно избежать, заменив все точки на \S
:
\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z
Точку следует использовать только в том случае, если вы действительно хотите разрешить использование любого символа. В противном случае используйте (отрицательный) класс символов, чтобы ограничить ваше регулярное выражение только теми символами, которые действительно разрешены. Хотя в данном случае это не имеет большого значения, не использовать точку, когда что-то более подходящее - очень хорошая привычка. Я вижу слишком много случаев катастрофического отката, потому что разработчик был слишком ленив, чтобы использовать что-то более подходящее, чем точка.
Поскольку есть большая вероятность, что начальные тесты найдут подходящий символ в первой половине пароля, ленивый квантификатор может быть более эффективным:
\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z
Но теперь перейдем к действительно важному вопросу: ни в одном из ответов не упоминается тот факт, что исходный вопрос, похоже, написан кем-то, кто мыслит в ASCII. Но в Java строки - это Unicode. Допускаются ли в паролях символы, отличные от ASCII? Если да, то запрещены только пробелы ASCII или все пробелы Unicode должны быть исключены.
По умолчанию \s
соответствует только пробелам ASCII, поэтому его инверсия \S
соответствует всем символам Unicode (пробелам или нет) и всем непробельным символам ASCII. Если символы Unicode разрешены, а пробелы Unicode - нет, UNICODE_CHARACTER_CLASS
можно указать флаг, чтобы \S
исключить пробелы Unicode. Если символы Юникода недопустимы, [\x21-\x7E]
их можно использовать вместо\S
сопоставления всех символов ASCII, не являющихся пробелами или управляющими символами.
Это подводит нас к следующей потенциальной проблеме: хотим ли мы разрешить использование управляющих символов? Первый шаг в написании правильного регулярного выражения - точно указать, что вы хотите сопоставить, а что нет. Единственный 100% технически правильный ответ состоит в том, что спецификация пароля в вопросе неоднозначна, поскольку в нем не указано, разрешены ли определенные диапазоны символов, такие как управляющие символы или символы, отличные от ASCII.