Développer une application avec le framework Play !


précédentsommairesuivant

VI. Définition d'un captcha

La façon dont nous avons implémenté l'ajout de commentaires permet à n'importe qui d'en publier. Afin de nous protéger des spams automatiques nous allons utiliser la méthode de l'image captcha.

VI-A. Génération de l'image captcha

Débutons en regardant comment nous pouvons générer une image captcha avec Play. Très simplement, nous allons utiliser une nouvelle action, sauf que celle-ci retournera un flux binaire au lieu d'une réponse HTML, comme c'était le cas auparavant.

Play offre un certain nombre d'outils qui peuvent s'avérer très utiles lors d'un développement web. La génération d'un captcha en fait justement partie. Nous pouvons ainsi utiliser l'utilitaire play.libs.Images pour générer cette image, puis pour l'insérer dans notre réponse HTTP.

Comme toujours, nous créerons une implémentation simple. Ajoutons l'action captcha dans le contrôleur (n'oublions pas d'ajouter l'import sur play.libs.*) :

 
Sélectionnez
public static void captcha() {
    Images.Captcha captcha = Images.captcha();
    renderBinary(captcha);
}

Il est à noter que nous pouvons passer l'objet binaire directement à la méthode renderBinary() car la classe Images.Captcha implémente l'interface java.io.InputStream.

Ajoutons une nouvelle route dans notre fichier de routage /yabe/conf/routes :

 
Sélectionnez
GET     /captcha                                Application.captcha

Voyons le résultat en nous rendant à la page http://localhost:9000/captcha :

Image non disponible

Si tout fonctionne correctement, à chaque rafraichissement de cette page, une nouvelle image doit être générée.

VI-B. Comment gérer l'état ?

Jusqu'à présent, ce que nous avons fait était relativement simple. La partie complexe de notre application arrive ! Pour valider le captcha, nous devons conserver quelque part le texte généré aléatoirement et affiché dans l'image. De cette façon, nous serons à même de valider la valeur saisie par le visiteur au moment de la soumission du formulaire.

Bien sûr, nous pourrions simplement ajouter le texte dans la session de l'utilisateur au moment de la génération de l'image, afin de la récupérer plus tard. Cependant, cette solution a deux revers :

  • Premier point : la session Play est stockée dans un cookie. Cela résout un certain nombre de problèmes en termes d'architecture, mais cela a aussi des impacts. Les données écrites dans le cookie de session sont signées (de façon à ce que l'utilisateur ne puisse pas les modifier) mais elles ne sont pas cryptées. Ainsi, si nous écrivons le code du captcha dans la session, n'importe qui pourra le retrouver facilement simplement en lisant le contenu du cookie !
  • Second point : Play est un framework sans état. Nous voulons donc gérer les choses de cette façon. Par exemple, que va t'il se passer si un même utilisateur ouvre simultanément deux pages différentes de notre blog, avec bien sûr, deux images captcha différentes ? Nous devons donc conserver le code du captcha pour chacun des formulaires générés.

Pour résoudre notre problème, nous avons besoin de deux choses. Nous allons stocker le code du captcha au niveau du serveur. Il s'agit ici de données éphémères (transient), c'est-à-dire qu'elles ne seront pas persistées en base. Nous pouvons donc utiliser le cache de Play. De plus, comme les données mises en cache ont une durée de vie limitée, cela permettra d'ajouter une sécurité supplémentaire (disons que le captcha ne sera ainsi valide que durant un petit laps de temps). Pour être capable de retrouver le code du captcha, nous devons générer un ID unique. Celui-ci sera ajouté dans le formulaire dans un champ caché, et fera ainsi implicitement référence au code du captcha.

C'est ainsi que nous résolvons élégamment notre problème d'absence d'état.

Modifions désormais notre action captcha (ne pas oublier l'import de play.cache.*) :

 
Sélectionnez
public static void captcha(String id) {
    Images.Captcha captcha = Images.captcha();
    String code = captcha.getText("#E4EAFD");
    Cache.set(id, code, "10mn");
    renderBinary(captcha);
}

Notons que la méthode getText() prend en paramètre n'importe quelle couleur, couleur qui sera utilisée pour dessiner le texte dans l'image.

VI-C. Ajout du code du captcha dans le formulaire

A chaque génération du formulaire de création de commentaire, nous créons un ID unique. Nous sommes donc aussi amenés à modifier notre code HTML pour intégrer cette information dans un champs caché. Voyons d'abord notre action Application.show :

 
Sélectionnez
public static void show(Long id) {
    Post post = Post.findById(id);
    String randomID = Codec.UUID();
    render(post, randomID);
}

Maintenant, occupons-nous du fichier de template /yabe/app/views/Application/show.html :

 
Sélectionnez
...
<p>
    <label for="content">Your message: </label>
    <textarea name="content" id="content">${params.content}</textarea>
</p>
<p>
    <label for="code">Please type the code below: </label>
    <img src="@{Application.captcha(randomID)}" />
    <br />
    <input type="text" name="code" id="code" size="18" value="" />
    <input type="hidden" name="randomID" value="${randomID}" />
</p>
<p>
    <input type="submit" value="Submit your comment" />
</p>
...

Bon début ! Notre formulaire intègre désormais l'image captcha :

Image non disponible

VI-D. Validation du captcha

Il ne reste alors plus qu'à valider le captcha. Nous avions ajouté un ID aléatoire dans un champ caché de notre formulaire. Ainsi, nous pouvons le récupérer dans l'action postComment, tout comme nous allons retrouver le code depuis notre cache. Nous pourrons finalement les comparer.

 
Sélectionnez
public static void postComment(
        Long postId, 
        @Required(message="Author is required") String author, 
        @Required(message="A message is required") String content, 
        @Required(message="Please type the code") String code, 
        String randomID) {
    Post post = Post.findById(postId);
    validation.equals(
        code, Cache.get(randomID)
    ).message("Invalid code. Please type it again");
    if(validation.hasErrors()) {
        render("Application/show.html", post, randomID);
    }
    post.addComment(author, content);
    flash.success("Thanks for posting %s", author);
    show(postId);
}

Comme nous avons de nouveaux messages d'erreur, nous modifions également la façon dont ils sont affichés dans notre template show.html (dans notre cas, nous n'affichons que la première erreur, ce qui est amplement suffisant) :

 
Sélectionnez
...
#{ifErrors}
    <p class="error">
        ${errors[0]}
    </p>
#{/ifErrors}
...

Généralement, dans les formulaires plus complexes, les messages d'erreur ne sont pas gérés de cette façon, mais plutôt externalisés dans un fichier de propriétés.

Nous sommes prêts à vérifier que le captcha est désormais complètement fonctionnel :

Image non disponible

Voilà qui est parfait !


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.