De plus en plus de personnes s'intéressent à Scala (comme moi), plutôt que de poser une question, j'aimerais discuter d'une implémentation d'un extrait de code de connexion/déconnexion pour une webapp basée sur Lift.
Je viens juste de commencer à apprendre Scala et Lift donc ce n'est probablement pas la meilleure façon d'implémenter une telle fonctionnalité mais j'aimerais la partager pour d'autres débutants et en discuter avec des développeurs plus expérimentés. Veuillez noter que je ne suis pas non plus un expert en développement web. Toute aide pour les améliorations serait grandement appréciée (surtout celles liées aux performances et à la sécurité) ;-)
1) Tout d'abord, l'extrait doit pouvoir être facilement plugable, avec 1 seule ligne de code dans votre modèle par défaut. Je l'ai fait en utilisant la fonctionnalité embedded de Lift (remarquez le trait bas pour qu'il ne puisse pas être rendu en tant que page en lui-même mais seulement invoqué à partir d'une page rendue, en bref, une sorte d'extrait "privé"):
2) Ensuite, dans _logInForm.html, j'utilise le balisage ci-dessous et un affichage conditionnel pour gérer tout :
Nom d'utilisateur :
Mot de passe :
Connecté en tant que .
3) ... et maintenant la logique Scala/Lift derrière ce balisage :
object LogInForm {
private object name extends SessionVar("")
private object password extends RequestVar("")
private object referer extends RequestVar(S.referer openOr "/")
var isLoggedIn = false
def loggedIn(html: NodeSeq) =
if (isLoggedIn) html else NodeSeq.Empty
def loggedOut(html: NodeSeq) =
if (!isLoggedIn) html else NodeSeq.Empty
def logIn = {
def processLogIn() {
Validator.isValidName(name) match {
case true => {
Validator.isValidLogin(name, password) match {
case true => { isLoggedIn = true } // Succès : connecté
case _ => S.error("password", "Nom d'utilisateur/mot de passe invalide !")
}
}
case _ => S.error("name", "Format de nom d'utilisateur invalide !")
}
}
val r = referer.is
"name=name" #> SHtml.textElem(name) &
"name=password" #> (
SHtml.textElem(password) ++
SHtml.hidden(() => referer.set(r))) &
"type=submit" #> SHtml.onSubmitUnit(processLogIn)
}
def logOut = {
def processLogOut() { isLoggedIn = false }
val r = referer.is
"type=submit" #> SHtml.onSubmitUnit(processLogOut)
}
def getName = "*" #> name.is
}
Commentaires :
- La sélection entre les deux formulaires est faite par la logique, rendant soit le balisage fourni, soit NodeSeq.Empty, en fonction du fait que l'utilisateur soit connecté ou déconnecté.
- J'ai utilisé Lift:Msg pour avoir des messages d'erreur à côté des champs appropriés (nom/mot de passe). Le message est envoyé en utilisant S.error dans la logique derrière et l'id approprié.
- J'effectue en fait les vérifications dans un assistant Validator en utilisant des expressions régulières et des vérifications de formats, etc. Cela renvoie un booléen à chaque fois, donc le pattern matching est trivial.
- J'utilise le referer pour que l'utilisateur puisse se connecter / se déconnecter tout en restant sur la même page.
- Le nom d'utilisateur est conservé en tant que variable de session et affiché lorsqu'il est connecté.
4) Vous pouvez contrôler l'accès à d'autres pages en faisant ce qui suit dans Boot.scala :
def sitemap() = SiteMap(
Menu("Accueil") / "index",
Menu("Page protégée") / "nomDeLaPageProtégée" >> If(() => LogInForm.isLoggedIn, ""),
// etc.
Questions :
- Aucune protection SSL (cela pourrait être une amélioration, je n'ai pas encore regardé cela en Scala/Lift). Toute expérience d'une autre personne pourrait être utile ?
- Utilisation de variable de session. Peut-être qu'il y a une meilleure façon de conserver l'état en Scala/Lift ?
- Y a-t-il déjà quelque chose spécialement conçu pour se connecter/déconnecter dans Lift que j'aurais pu manquer ?
- Il n'est pas très long, mais pourrait peut-être être plus compact ? (J'aimerais ne pas sacrifier trop de lisibilité cependant. Les autres développeurs doivent le comprendre rapidement)
- Toute autre suggestion ?
À bientôt,
Marc.