Tất cả các câu trả lời đã cho trước đó sử dụng cùng một kỹ thuật (đúng) để sử dụng một cái nhìn riêng cho từng yêu cầu. Nhưng chúng chứa một vài điểm kém hiệu quả và một lỗi lớn tiềm ẩn, tùy thuộc vào back end sẽ thực sự sử dụng mật khẩu.
Tôi sẽ bắt đầu với regex từ câu trả lời được chấp nhận:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$
Trước hết, vì Java hỗ trợ \A
và \z
tôi thích sử dụng những thứ đó để đảm bảo toàn bộ chuỗi được xác thực, độc lập với Pattern.MULTILINE
. Điều này không ảnh hưởng đến hiệu suất, nhưng tránh sai lầm khi regexes được tái chế.
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z
Kiểm tra mật khẩu không chứa khoảng trắng và kiểm tra độ dài tối thiểu của nó có thể được thực hiện trong một lần chuyển bằng cách sử dụng tất cả cùng một lúc bằng cách đặt định lượng biến đổi {8,}
trên tốc ký \S
giới hạn các ký tự được phép:
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z
Nếu mật khẩu được cung cấp có chứa dấu cách, tất cả các lần kiểm tra sẽ được thực hiện, chỉ để lần kiểm tra cuối cùng không thành công trên khoảng trống. Điều này có thể tránh được bằng cách thay thế tất cả các dấu chấm bằng \S
:
\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z
Dấu chấm chỉ nên được sử dụng nếu bạn thực sự muốn cho phép bất kỳ ký tự nào. Nếu không, hãy sử dụng một lớp ký tự (bị phủ định) để giới hạn regex của bạn chỉ với những ký tự thực sự được phép. Mặc dù nó có chút khác biệt trong trường hợp này, nhưng không sử dụng dấu chấm khi thứ khác thích hợp hơn là một thói quen rất tốt. Tôi thấy có quá nhiều trường hợp quay trở lại thảm khốc vì nhà phát triển quá lười biếng để sử dụng thứ gì đó phù hợp hơn dấu chấm.
Vì có nhiều khả năng các thử nghiệm ban đầu sẽ tìm thấy một ký tự thích hợp trong nửa đầu của mật khẩu, một bộ định lượng lười biếng có thể hiệu quả hơn:
\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z
Nhưng bây giờ đối với vấn đề thực sự quan trọng: không có câu trả lời nào đề cập đến thực tế rằng câu hỏi ban đầu dường như được viết bởi ai đó nghĩ trong ASCII. Nhưng trong Java chuỗi là Unicode. Các ký tự không phải ASCII có được phép trong mật khẩu không? Nếu có, thì chỉ những khoảng trắng ASCII không được phép hoặc nên loại trừ tất cả khoảng trắng Unicode.
Theo mặc định, \s
chỉ khớp với khoảng trắng ASCII, vì vậy nghịch đảo của nó \S
khớp với tất cả các ký tự Unicode (khoảng trắng hoặc không) và tất cả các ký tự ASCII không phải khoảng trắng. Nếu các ký tự Unicode được cho phép nhưng không có dấu cách Unicode, thì UNICODE_CHARACTER_CLASS
cờ có thể được chỉ định để \S
loại trừ khoảng trắng Unicode. Nếu các ký tự Unicode không được phép, thì [\x21-\x7E]
có thể được sử dụng thay thế \S
để khớp với tất cả các ký tự ASCII không phải là khoảng trắng hoặc ký tự điều khiển.
Điều này đưa chúng ta đến vấn đề tiềm năng tiếp theo: chúng ta có muốn cho phép các ký tự điều khiển không? Bước đầu tiên để viết một regex thích hợp là xác định chính xác những gì bạn muốn khớp và những gì bạn không. Câu trả lời duy nhất đúng 100% về mặt kỹ thuật là thông số kỹ thuật mật khẩu trong câu hỏi không rõ ràng vì nó không nêu rõ liệu một số ký tự nhất định như ký tự điều khiển hoặc ký tự không phải ASCII có được phép hay không.