Expresión regular para imponer contraseñas complejas, haciendo coincidir 3 de 4 reglas

Tengo los siguientes criterios para crear una expresión regular para una contraseña que cumpla con las siguientes reglas:

  1. La contraseña debe tener 8 caracteres (esto puedo hacer :-)).

La contraseña debe contener caracteres de al menos 3 de las siguientes 4 reglas:

  1. Mayúsculas
  2. Minúscula
  3. Números
  4. No alfanumérico

Puedo hacer que la expresión coincida con TODAS esas reglas con la siguiente expresión:

/^(?=.*\d)(?=.*[az])(?=.*[AZ])(?=.[\W]).{8,}$/ 

Pero estoy luchando con la forma de hacerlo de tal manera que solo necesita resolver 3 de las 4 reglas.

¿Puede alguien ayudarme con esto?

No use una expresión regular para verificarlo.

 if (password.length < 8) alert("bad password"); var hasUpperCase = /[AZ]/.test(password); var hasLowerCase = /[az]/.test(password); var hasNumbers = /\d/.test(password); var hasNonalphas = /\W/.test(password); if (hasUpperCase + hasLowerCase + hasNumbers + hasNonalphas < 3) alert("bad password"); 

Si debe usar una sola expresión regular:

 ^(?:(?=.*[az])(?:(?=.*[AZ])(?=.*[\d\W])|(?=.*\W)(?=.*\d))|(?=.*\W)(?=.*[AZ])(?=.*\d)).{8,}$ 

Esta expresión regular no está optimizada para la eficiencia. Está construido por A·B·C + A·B·D + A·C·D + B·C·D con cierta factorización. Descompostura:

 ^ (?: (?=.*[az]) # 1. there is a lower-case letter ahead, (?: # and (?=.*[AZ]) # 1.ai) there is also an upper-case letter, and (?=.*[\d\W]) # 1.a.ii) a number (\d) or symbol (\W), | # or (?=.*\W) # 1.bi) there is a symbol, and (?=.*\d) # 1.b.ii) a number ahead ) | # OR (?=.*\W) # 2.a) there is a symbol, and (?=.*[AZ]) # 2.b) an upper-case letter, and (?=.*\d) # 2.c) a number ahead. ) .{8,} # the password must be at least 8 characters long. $ 

Podrías escribir una expresión regular muy sofisticada para hacer eso. En cambio, sugeriría escribir cuatro expresiones regulares distintas, una para cada regla, y probarlas una a una, contando cuántas de ellas coincidían. Si tres de cuatro lo hicieron, acepte la contraseña.

Puede usar la siguiente Regex:

 (^(?=.*\d)(?=.*[az])(?=.*[AZ]).*$)?(^(?=.*\d)(?=.*[az])(?=.*[@#$%^&+=]).*$)?(^(?=.*\d)(?=.*[AZ])(?=.*[@#$%^&+=]).*$)?(^(?=.*[az])(?=.*[AZ])(?=.*[@#$%^&+=]).*$)? 

Con una longitud mínima de contraseña de 8 y una longitud máxima de 32, puede usar la siguiente Regex:

 (^(?=.*\d)(?=.*[az])(?=.*[AZ]).{8,32}$)?(^(?=.*\d)(?=.*[az])(?=.*[@#$%^&+=]).{8,32}$)?(^(?=.*\d)(?=.*[AZ])(?=.*[@#$%^&+=]).{8,32}$)?(^(?=.*[az])(?=.*[AZ])(?=.*[@#$%^&+=]).{8,32}$)? 

Id sugiero hacer los controles por separado, y luego simplemente sumr cuántos coinciden.

(Tampoco usaría una expresión regular en ninguno de ellos, pero eso es solo mi punto de vista personal, es decir, que dificultan la legibilidad y, en general, son código de escritura única)