119 votes

Regexp Java pour la validation des mots de passe

Je suis en train de créer un regexp pour la validation des mots de passe qui sera utilisé dans une application Java comme paramètre de configuration.

Le regexp est :

^.*(?=.{8,})(?=..*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$

La politique de mot de passe est :

  • Au moins 8 caractères

  • Contient au moins un chiffre

  • Contient au moins un caractère alpha inférieur et un caractère alpha supérieur.

  • Contient au moins un caractère dans un ensemble de caractères spéciaux ( @#%$^ etc.)

  • Ne contient pas d'espace, de tabulation, etc.

Il ne me manque que le point 5. Je n'arrive pas à faire vérifier par la regexp les espaces, les tabulations, les retours de chariot, etc.

Quelqu'un peut-il m'aider ?

4 votes

Les règles relatives aux mots de passe sont mauvaises. Veuillez consulter Référence - Validation du mot de passe pour plus d'informations.

358voto

Tomalak Points 150423

Essayez ça :

^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$

Explication :

^                 # start-of-string
(?=.*[0-9])       # a digit must occur at least once
(?=.*[a-z])       # a lower case letter must occur at least once
(?=.*[A-Z])       # an upper case letter must occur at least once
(?=.*[@#$%^&+=])  # a special character must occur at least once
(?=\S+$)          # no whitespace allowed in the entire string
.{8,}             # anything, at least eight places though
$                 # end-of-string

Il est facile d'ajouter, de modifier ou de supprimer des règles individuelles, car chaque règle est un "module" indépendant.

El (?=.*[xyz]) mange la chaîne entière ( .* ) et revient en arrière jusqu'à la première occurrence où [xyz] peuvent correspondre. Elle réussit si [xyz] est trouvé, sinon il échoue.

L'alternative serait d'utiliser un qualificatif réticent : (?=.*?[xyz]) . Pour une vérification de mot de passe, cela ne fera guère de différence, pour des chaînes beaucoup plus longues, cela pourrait être la variante la plus efficace.

La variante la plus efficace (mais la plus difficile à lire et à maintenir, donc la plus sujette aux erreurs) serait la suivante (?=[^xyz]*[xyz]) bien sûr. Pour une regex de cette longueur et dans ce but, je ne recommanderais pas de le faire de cette façon, car cela n'a pas de réels avantages.

17 votes

@Kerby82 : Dans les chaînes de caractères Java, les antislashes doivent être échappés. Essayez d'utiliser \\s . C'est une exigence de Java, pas une exigence de regex.

0 votes

Oui \t est inutile puisque \s include Un caractère d'espacement : [ \t\n\x0B\f\r ]. Mais le backslashing du \s maintenant en java fonctionne parfaitement. Merci beaucoup !

0 votes

Vous pouvez désactiver le mode multi-ligne explicitement dans le cadre de la regex si vous en avez besoin en utilisant (?-m : ...) autour de tout le reste.

62voto

agiles Points 483

Exemple simple utilisant regex

public class passwordvalidation {
    public static void main(String[] args) {
      String passwd = "aaZZa44@"; 
      String pattern = "(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}";
      System.out.println(passwd.matches(pattern));
   }
}

Explications :

  • (?=.*[0-9]) un chiffre doit apparaître au moins une fois
  • (?=.*[a-z]) une lettre minuscule doit apparaître au moins une fois
  • (?=.*[A-Z]) une lettre majuscule doit apparaître au moins une fois
  • (?=.*[@#$%^&+=]) un caractère spécial doit apparaître au moins une fois
  • (?=\\S+$) aucun espace n'est autorisé dans la chaîne entière
  • .{8,} au moins 8 caractères

5 votes

.{5,10} représente un minimum de 5 caractères et un maximum de 10 caractères. Juste au cas où quelqu'un chercherait une explication spécifique.

0 votes

@iabhi, c'est ce que je cherchais. Merci.

0 votes

J'essaie d'implémenter Regex sur un mot de passe stocké dans un tableau de caractères au lieu d'une chaîne pour des raisons de sécurité. Mais comment appliquer une Regex à un tableau de caractères ?

14voto

Jan Goyvaerts Points 10402

Toutes les réponses données précédemment utilisent la même technique (correcte) pour utiliser un lookahead séparé pour chaque exigence. Mais elles contiennent quelques inefficacités et un bogue potentiellement important, en fonction du back-end qui utilisera effectivement le mot de passe.

Je vais commencer par la regex de la réponse acceptée :

^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$

Tout d'abord, puisque Java supporte \A y \z Je préfère les utiliser pour m'assurer que la chaîne entière est validée, indépendamment de Pattern.MULTILINE . Cela n'affecte pas les performances, mais évite les erreurs lors du recyclage des regex.

\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z

La vérification que le mot de passe ne contient pas d'espace et la vérification de sa longueur minimale peuvent être effectuées en une seule passe en utilisant le quantificateur variable "tout à la fois". {8,} sur la sténographie \S qui limite les caractères autorisés :

\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z

Si le mot de passe fourni contient un espace, toutes les vérifications seront effectuées, mais la vérification finale échouera à cause de l'espace. Ceci peut être évité en remplaçant tous les points par \S :

\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z

Le point ne doit être utilisé que si vous voulez vraiment autoriser n'importe quel caractère. Sinon, utilisez une classe de caractères (négation) pour limiter votre regex aux seuls caractères qui sont réellement autorisés. Bien que cela fasse peu de différence dans ce cas, ne pas utiliser le point lorsque quelque chose d'autre est plus approprié est une très bonne habitude. Je vois beaucoup trop de cas de retour en arrière catastrophique parce que le développeur était trop paresseux pour utiliser quelque chose de plus approprié que le point.

Comme il y a de fortes chances que les tests initiaux trouvent un caractère approprié dans la première moitié du mot de passe, un quantificateur paresseux peut être plus efficace :

\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z

Mais passons maintenant à la question vraiment importante : aucune des réponses ne mentionne le fait que la question originale semble avoir été écrite par quelqu'un qui pense en ASCII. Mais en Java, les chaînes de caractères sont Unicode. Les caractères non ASCII sont-ils autorisés dans les mots de passe ? Si c'est le cas, seuls les espaces ASCII sont-ils interdits, ou tous les espaces blancs Unicode doivent-ils être exclus ?

Par défaut \s ne correspond qu'aux espaces blancs ASCII, donc son inverse \S correspond à tous les caractères Unicode (avec ou sans espace) et à tous les caractères ASCII sans espace. Si les caractères Unicode sont autorisés mais que les espaces Unicode ne le sont pas, la fonction UNICODE_CHARACTER_CLASS peut être spécifié pour que \S exclure les espaces blancs Unicode. Si les caractères Unicode ne sont pas autorisés, alors [\x21-\x7E] peut être utilisé à la place de \S pour correspondre à tous les caractères ASCII qui ne sont pas un espace ou un caractère de contrôle.

Ce qui nous amène au problème potentiel suivant : voulons-nous autoriser les caractères de contrôle ? La première étape dans l'écriture d'une regex correcte est de spécifier exactement ce que vous voulez faire correspondre et ce que vous ne voulez pas. La seule réponse techniquement correcte à 100 % est que la spécification du mot de passe dans la question est ambiguë car elle ne précise pas si certaines plages de caractères comme les caractères de contrôle ou les caractères non ASCII sont autorisés ou non.

13voto

Martin Rauscher Points 522

Vous ne devez pas utiliser de Regex trop complexes (si vous pouvez les éviter) car elles sont

  • difficile à lire (du moins pour tout le monde sauf vous-même)
  • difficile à prolonger
  • difficile à déboguer

Bien que l'utilisation d'un grand nombre de petites expressions régulières puisse entraîner un léger surcoût en termes de performances, les points ci-dessus le compensent aisément.

Je le mettrais en œuvre comme ceci :

bool matchesPolicy(pwd) {
    if (pwd.length < 8) return false;
    if (not pwd =~ /[0-9]/) return false;
    if (not pwd =~ /[a-z]/) return false;
    if (not pwd =~ /[A-Z]/) return false;
    if (not pwd =~ /[%@$^]/) return false;
    if (pwd =~ /\s/) return false;
    return true;
}

0 votes

Et du point de vue de la sécurité, il est de loin préférable d'imposer des mots de passe plus longs, d'empêcher les mots de passe bien connus (comme 12345 et pass=user) plutôt que de rendre les mots de passe super compliqués et difficiles à retenir.

0 votes

J'aime votre approche ci-dessus. Je vous en remercie !

5voto

surfealokesea Points 982

Merci pour toutes les réponses, basées sur toutes mais étendant les caractères spheciales :

@SuppressWarnings({"regexp", "RegExpUnexpectedAnchor", "RegExpRedundantEscape"})
String PASSWORD_SPECIAL_CHARS = "@#$%^`<>&+=\"!ºª·#~%&'¿¡€,:;*/+-.=_\\[\\]\\(\\)\\|\\_\\?\\\\";
int PASSWORD_MIN_SIZE = 8;
String PASSWORD_REGEXP = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[" + PASSWORD_SPECIAL_CHARS + "])(?=\\S+$).{"+PASSWORD_MIN_SIZE+",}$";

Unité testée :

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X