Todas las respuestas dadas anteriormente usan la misma técnica (correcta) para usar una búsqueda anticipada separada para cada requisito. Pero contienen un par de ineficiencias y un error potencialmente masivo, dependiendo del back-end que realmente usará la contraseña.
Comenzaré con la expresión regular de la respuesta aceptada:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$
En primer lugar, dado que Java es compatible \A
y \z
prefiero usarlos para asegurarme de que toda la cadena esté validada, independientemente de Pattern.MULTILINE
. Esto no afecta el rendimiento, pero evita errores cuando se reciclan las expresiones regulares.
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z
Verificar que la contraseña no contenga espacios en blanco y verificar su longitud mínima se puede hacer en una sola pasada usando todo a la vez colocando el cuantificador variable {8,}
en la abreviatura \S
que limita los caracteres permitidos:
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z
Si la contraseña proporcionada contiene un espacio, se realizarán todas las comprobaciones, solo para que la comprobación final falle en el espacio. Esto se puede evitar reemplazando todos los puntos con \S
:
\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z
El punto solo debe usarse si realmente desea permitir cualquier carácter. De lo contrario, use una clase de caracteres (negada) para limitar su expresión regular a solo aquellos caracteres que realmente están permitidos. Aunque hay poca diferencia en este caso, no usar el punto cuando otra cosa es más apropiada es un muy buen hábito. Veo demasiados casos de retroceso catastrófico porque el desarrollador fue demasiado vago para usar algo más apropiado que el punto.
Dado que hay una buena probabilidad de que las pruebas iniciales encuentren un carácter apropiado en la primera mitad de la contraseña, un cuantificador diferido puede ser más eficiente:
\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z
Pero ahora el tema realmente importante: ninguna de las respuestas menciona el hecho de que la pregunta original parece haber sido escrita por alguien que piensa en ASCII. Pero en Java, las cadenas son Unicode. ¿Se permiten caracteres que no son ASCII en las contraseñas? Si es así, solo se deshabilitan los espacios ASCII o se deben excluir todos los espacios en blanco Unicode.
De forma predeterminada \s
, solo coincide con los espacios en blanco ASCII, por lo que su inverso \S
coincide con todos los caracteres Unicode (espacios en blanco o no) y todos los caracteres ASCII que no son espacios en blanco. Si se permiten caracteres Unicode pero no los espacios Unicode, UNICODE_CHARACTER_CLASS
se puede especificar la marca para \S
excluir los espacios en blanco Unicode. Si no se permiten caracteres Unicode, entonces [\x21-\x7E]
se pueden usar en lugar de \S
para hacer coincidir todos los caracteres ASCII que no sean un espacio o un carácter de control.
Lo que nos lleva al siguiente problema potencial: ¿queremos permitir personajes de control? El primer paso para escribir una expresión regular adecuada es especificar exactamente lo que desea hacer coincidir y lo que no. La única respuesta 100% técnicamente correcta es que la especificación de la contraseña en la pregunta es ambigua porque no indica si ciertos rangos de caracteres como caracteres de control o caracteres no ASCII están permitidos o no.