Publié par
Il y a 8 années · 7 minutes · Front

Grails Spring Security Plugin, la sécurité facile

Le monde Grails a le vent en poupe ces temps-ci. Bien avant le printemps on peut voir fleurir des dizaines de tutoriaux sur le net pour prendre l’outil en main et créer sa première application. Mais un simple « Getting started » ne suffit pas toujours à bien prendre la mesure de la puissance de Grails.

Je vous propose donc un petit tutoriel pour mettre en place une gestion des utilisateurs, avec gestion des rôles, filtres URL/Rôles, formulaire de souscription de compte. Pour cela, nous allons nous appuyer sur le Spring Security Plugin de Grails. C’est une étape incontournable de la création d’application, et nous allons pouvoir constater à quel point l’utilisation de Grails booste la productivité.

Béni soit le scaffolding

Commençons par créer l’application BookStore qui nous servira de base, ainsi qu’une classe de domaine Book.

grails create-app BookStore
cd BookStore
grail create-domain-class fr.xebia.bookstore.common.Book

Apportons quelques modifications à notre classe pour qu’elle soit un peu plus parlante dans la suite des exemples :

grails-app/domain/fr/xebia/bookstore/common/Book.groovy

package fr.xebia.bookstore.common

class Book {

    String titre
    String auteur
    Date premierePublication

    static constraints = {
        titre(nullable:false)
        auteur(nullable:false)
    }
}

Maintenant nous générons le CRUD autour de notre classe Book.

grails generate-all fr.xebia.bookstore.common.Book

Installation du plugin Spring Security. Pas de panique, acegi est toujours le nom du plugin, c’est un héritage de l’histoire

grails install-plugin acegi

Creation des classes de domaines pour la gestion de la sécurité, Utilisateur, roles et mapping de security et génération des managers associés

grails create-auth-domains             
      fr.xebia.bookstore.security.User 
      fr.xebia.bookstore.security.Role 
      fr.xebia.bookstore.security.RequestMap
grails generate-manager

Maintenant nous générons les composants nécessaires à la souscription d’un compte par un internaute.

grails generate-registration

A ce stade, nous disposons des contrôleurs suivants (et des vues qui vont avec) :

  • CaptchaController : permet de générer des images captcha pour éviter que des robots ne viennent créer des comptes utilisateurs
  • fr.xebia.bookstore.common.BookController : CRUD de gestion de la classe Book
  • LoginController : Contrôleur de connexion
  • LogoutController : Contrôleur de déconnexion
  • RegisterController : Contrôleur d’enregistrement d’un compte utilisateur par un internaute
  • RequestMapController : CRUD de gestion des règles de sécurité
  • RoleController : CRUD de gestion des rôles
  • UserController : CRUD de gestion des utilisateurs, destiné à notre espace d’administration

On peut déjà démarrer l’application avec un grails run-app et voir ce que ça donne :

grails_tuto_1

Configuration et sécurisation

Cela fait déjà beaucoup de matières à exploiter en un temps relativement court. Mais en l’état c’est encore un peu brut de fonderie pour ressembler à une vraie application. Nous allons donc faire un peu de configuration et de rangement autour de tout ce code parachuté par Grails. Pour atteindre notre objectif, nous allons maintenant procéder à la sécurisation des écrans de :

  • CRUD complet des utilisateurs
  • CRUD complet des rôles
  • CRUD complet des mappings de sécurité
  • Création de Book

Tout est déjà en place pour cela grâce au code que nous avons généré. Commençons par utiliser le RoleController pour créer un rôle utilisateur et un rôle administrateur :

grails_tuto_2

Maintenant que nous avons des rôles, il faut créer un compte utilisateur avec le rôle ROLE_ADMIN pour éviter de se retrouver bloqué quand on posera les mappings de sécurité. Cette fois, c’est du coté des écrans du UserController que ça se passe :

grails_tuto_3

Et pour finir, on pose des mappings de sécurité sur les URL qui nous intéressent, avec la liste des rôles autorisés à accéder. Pour cela, nous utilisons le RequestMapController :

grails_tuto_4

Et voilà ! Notre application répond déjà mieux à nos attentes en terme de sécurité :

  • tout le monde peut voir la liste des Book présents en base.
  • seuls les utilisateur ayant le rôle ROLE_USER ou ROLE_ADMIN peuvent insérer de nouveaux Book
  • seuls les utilisateur ayant le rôle ROLE_ADMIN peuvent créer des utilisateurs, des rôles et modifier les règles de sécurité

Si un utilisateur non authentifié tente d’accéder à une URL protégée, il sera automatiquement redirigé vers un écran de login avant d’accéder à la page désirée.

Adaptations de confort

Par défaut, une application Grails utilise HSQLDB et démarre avec une base vierge à chaque lancement. Nous allons donc changer cela pour intégrer directement à chaque démarrage les rôles, mappings de sécurité et un compte administrateur :

grails-app/conf/BootStrap.groovy

import fr.xebia.bookstore.security.User
import fr.xebia.bookstore.security.Role
import fr.xebia.bookstore.security.RequestMap

class BootStrap {

    def authenticateService
    
    def init = { servletContext ->

        def roleAdmin = new Role(authority: 'ROLE_ADMIN', description: 'Administrateur').save()
        def roleUser = new Role(authority: 'ROLE_USER', description: 'Utilisateur').save()

        def userAdmin = new User(username: 'admin', userRealName: 'ATTAN Charles',
            passwd: authenticateService.encodePassword('admin'),
            enabled: true, email: 'admin@bookstore.fr')

        userAdmin.addToAuthorities(roleAdmin)
        userAdmin.save()

        def protectBookCreation = new RequestMap(url: '/book/create*', configAttribute: 'ROLE_ADMIN,ROLE_USER').save()
        def protectUserManaging = new RequestMap(url: '/user/*', configAttribute: 'ROLE_ADMIN').save()
        def protectRoleManaging = new RequestMap(url: '/role/*', configAttribute: 'ROLE_ADMIN').save()
        def protectSecurityMappingManaging = new RequestMap(url: '/requestMap/*', configAttribute: 'ROLE_ADMIN').save()
    }
    
    def destroy = {
    }
} 

Voilà, au prochain démarrage nous retrouverons toutes nos chères données directement en base. Ensuite, nous pouvons nous attaquer à l’enregistrement des internautes en tant qu’utilisateur standard. Par défaut, le formulaire lié au RegisterController crée des utilisateurs sans rôle, ce qui ne nous convient pas du tout. De plus, comme il serait agréable pour les nouveaux utilisateurs de recevoir un mail de confirmation de leur inscription, on va le mettre en place dans la foulée :

grails-app/conf/SecurityConfig.groovy

security {

    active = true

    loginUserDomainClass    = "fr.xebia.bookstore.security.User"
    authorityDomainClass    = "fr.xebia.bookstore.security.Role"
    requestMapClass         = "fr.xebia.bookstore.security.RequestMap"

        defaultRole             = 'ROLE_USER'   // Role par defaut des nouveaux utilisateurs
        useMail                 = true          // specifie d'envoyer un mail à l'enregistrement d'un compte
        mailHost                = 'localhost'   // serveur d'envoi de mails
        mailProtocol            = 'smtp'        // nom du protocole JavaMail
        mailFrom                = 'no.reply@bookstore.fr'   // adresse de l'envoyeur du mail
        mailPort                = 25            // port de connexion au serveur de mail
}

Avec ces modifications, tous les internautes qui créeront des comptes par le formulaire obtiendrons le rôle ROLE_USER et recevront un mail comme ceci :

grails_tuto_5

Si vous souhaitez adapter un peu le format du mail, le code associé est situé vers la ligne 170 du code de RegisterController :

extrait de grails-app/controllers/RegisterController.groovy

[...]
if (config.security.useMail) {
                String emailContent = """You have signed up for an account at:

 ${request.scheme}://${request.serverName}:${request.serverPort}${request.contextPath}

 Here are the details of your account:
 -------------------------------------
 LoginName: ${person.username}
 Email: ${person.email}
 Full Name: ${person.userRealName}
 Password: ${params.passwd}
"""

                def email = [
                    to: [person.email], // 'to' expects a List, NOT a single email address
                    subject: "[${request.contextPath}] Account Signed Up",
                    text: emailContent // 'text' is the email body
                ]
                emailerService.sendEmails([email])
            }
[...]

Et voilà comment en quelques minutes on peut avoir une application, basique certes… mais sécurisée !

Pour aller plus loin

Le plugin Grails Spring Security possède de nombreuses autres options. Je n’ai développé ici que la stratégie de stockage des règles de sécurité dans la base, mais il est également possible de les définir directement dans le fichier SecurityConfig, ou par annotations des méthodes des contrôleurs.

On peut appuyer la sécurité sur OpenID, LDAP, CAS, NTLM ou même utiliser une connexion FaceBook Connect. On peut changer l’algorithme de cryptage des mots de passe, les possibilités sont assez étendues.

Et si vraiment Spring Security ne vous convient pas, il existe d’autres solutions de sécurité à base de plugins comme Shiro ou Stark, qui feront peut-être l’objet d’autres articles dans le futur.

Conclusion

L’utilisation du plugin Grails Spring Security permet de mettre le pied à l’étrier très rapidement, en s’appuyant sur un standard des frameworks de sécurité en Java. J’ai personnellement beaucoup apprécié la simplicité de prise en main et les exemples fournis dans la documentation officielle. Je vous invite d’ailleurs à la parcourir, elle contient d’autres tutoriels notamment pour l’utilisation de OpenID et LDAP.

Nota Bene : Cet article est fortement inspiré de celui-ci, mais il méritait des précisions à mon goût.

Ressources :

Aurélien Maury
Aurélien est passionné par les frameworks web haute productivité comme Grails, JRuby on Rails ou Play! framework. Il est également intéressé par tous les aspects de performance et d'optimisation (mais aussi par la phytothérapie, la PNL, la basse électrique, la philosophie et pleins d'autres sujets). Il est également formateur au sein de Xebia Training .

11 réflexions au sujet de « Grails Spring Security Plugin, la sécurité facile »

  1. Publié par Stéphane Maldini, Il y a 8 années

    Attention toutefois le plugin n’a pas évolué depuis plusieurs mois. On a bon espoir que le développement reprenne avec la récente intégration d’un des auteurs dans l’équipe core grails.
    Il est trés intéressant d’étudier l’alternative Apache Shiro qui a l’avantage de proposer une sécurité ACL facile et l’implémentation de nouvelles sources d’authentification sympathique.

  2. Publié par Stan, Il y a 7 années

    Merci Bcp pour cet article qui me permet d’avoir une base sur Grails.
    Cependant, j’ai un petit souci : Je désire utiliser le smtp de gmail pour pouvoir
    envoyer le mail de confirmation mais j’ai cette erreur :

    « Could not connect to SMTP host: smtp.gmail.com, port: 465, response: -1 »

    Mon code dans grails-app/conf/SecurityConfig.groovy est le suivant:

    ********************************************************************
    defaultRole = ‘ROLE_USER’
    useMail = true
    mailUsername = ‘myLogin@gmail.com’
    mailPassword = ‘myPassword’
    mailHost = ‘smtp.gmail.com’
    mailProtocol = ‘smtp’

    mailFrom = ‘myLogin@gmail.com’
    mailPort = 465
    ********************************************************************

    J’ai parcouru la toile et toujours rien.
    Pourriez vous s’il vous plait m’accorder votre aide.

    Merci d’avance,

    Stan

  3. Publié par Stan, Il y a 7 années

    Hello à tous,
    solution trouvée :
    Pour Gmail
    Port 465 => POP
    Port 587 => SMTP

    NB: Ne pas oublier le mail properties
    ******************************************************************************
    useMail = true
    mailHost = ‘smtp.gmail.com’
    mailPort = 587
    mailUsername = ‘myLogin@gmail.com’
    mailPassword = ‘mmyPassword’
    mailProps = [« mail.smtp.auth »:’true’, »mail.smtp.socketFactory.port »:’587′, »mail.smtp.socketFactory.class »:’javax.net.ssl.SSLSocketFactory’, »mail.smtp.socketFactory.fallback »:’false’]
    mailFrom = ‘myLogin@gmail.com’
    ******************************************************************************

    Encore merci pour ce tuto ;-).A+

  4. Publié par Aurélien Maury, Il y a 7 années

    J’étais en train de rédiger mon coup de pouce pour GMail quand j’ai vu arrivé le dernier commentaire. Ravi de savoir que cet article a servi de façon concrète au lieu de simplement alimenter les archives du blog.

  5. Publié par Stan, Il y a 7 années

    Bonjour à tous,
    je reviens vers vous car j’ai une « petite » question…
    L’envoi de mail de confirmation fonctionne : Ok.

    Mais si à présent, il y a des plaisantins qui s’amusent
    à rentrer de fausses adresses …Pas cool!!

    Comment me serait il possible de mettre en place une validation de leurs
    comptes par mail??
    C’est à dire une fois qu’ils auront cliqué sur le lien que je leur ai envoyé.

    Votre aide me ferait un grand bien :-(..

    Merci d’avance

  6. Publié par Stan, Il y a 7 années

    :-) ;-)!!
    Bonjour à tous,
    Solution trouvée en utilisant le plugin email-confirmation.

    ce dernier envoi un lien sur lequel l’internaute devra cliquer pour
    confirmer son adresse mail.

    Une fois la confirmation effectuée, Acegi se charge du mail de confirmation
    certifiant que l’internaute peut se connecter au site.

    Portez vous bien
    Stan

  7. Publié par Stan, Il y a 7 années

    ;-) Hello,

    Suis actuelmnt sur un projet grails et là je me heurte à un problème en base de données :Je voudrai éviter que les éléments saisis par un utilisateur X soit visible par un utilisateur Y et vise versa mais j’y arrive pas.

    Mon soucis n’a rien à voir avec le tuto ci dessus et je m’en excuse cependant votre aide me fera le plus grand bien.

    Merci d’avance

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *