所有先前給出的答案使用相同的(正確的)技術爲每個需求使用單獨的前瞻。但是它們包含了一些效率低下和潛在的巨大bug,具體取決於實際使用密碼的後端。
我會從接受的答案正則表達式開始:
^(?=.*[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
的點,才應使用。否則,使用(否定)字符類將您的正則表達式限制爲僅允許那些真正允許的字符。雖然在這種情況下沒有什麼區別,但not using the dot when something else is more appropriate是一個很好的習慣。我看到太多的例子catastrophic backtracking,因爲開發者懶得使用比點更合適的東西。
因爲有一個很好的機會,在最初的測試將在密碼上半年找到一個合適的角色,一個懶惰的量詞可以更快捷:
\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空格。如果不允許Unicode字符,則可以使用[\x21-\x7E]
而不是\S
來匹配不是空格或控制字符的所有ASCII字符。
這給我們帶來了下一個潛在問題:我們想要允許控制字符嗎?編寫正確的正則表達式的第一步是準確地指定要匹配的內容以及不匹配的內容。唯一100%技術上正確的答案是,問題中的密碼規範是不明確的,因爲它沒有說明是否允許某些範圍的字符,如控制字符或非ASCII字符。
密碼規則是壞的。請參閱[參考 - 密碼驗證](https://stackoverflow.com/questions/48345922/reference-password-validation)以獲取更多信息。 – ctwheels