2011-05-23 16 views
5

Como cada vez más gente está interesada en Scala (como yo), en lugar de una pregunta, me gustaría discutir una implementación de un fragmento de inicio de sesión/cierre de sesión para una aplicación web basada en Lift.La mejor manera de crear un formulario de inicio/cierre de sesión en Scala usando Lift

Acabo de empezar a aprender Scala y Lift por lo que probablemente no sea la mejor manera de implementar una característica de este tipo, pero me gustaría compartirlo con otros principiantes y discutirlo con desarrolladores más experimentados. Tenga en cuenta que tampoco soy un experto en desarrollo web. Cualquier ayuda para la mejora sería muy apreciada (especialmente el rendimiento y los relacionados con la seguridad) ;-)

1) En primer lugar, el fragmento tiene que ser fácilmente enchufable, al igual que con 1 línea de código en su defecto modelo. Lo he hecho con el incrustado función de elevación (nótese el guión bajo por lo que no se puede representar como una página en sí, pero sólo invoca desde una página representada, en definitiva, una especie de fragmento de "privado"):

<lift:embed what="_logInForm" /> 

2) Luego, en _logInForm.html, utilizo el marcado de abajo y una pantalla condicionalpara manejar todo:

<div> 
    <!-- User is not logged in, show a form to log in using the method loggedOut --> 
    <lift:LogInForm.loggedOut> 
     <form class="lift:LogInForm.logIn?form=post"> 
      <label for="textName">Username: </label><input type="text" id="textName" name="name" /> <span class="lift:Msg?id=name;errorClass=error"/><br/> 
      <label for="textPassword">Password: </label><input type="password" id="textPassword" name="password" /> <span class="lift:Msg?id=password;errorClass=error"/><br/> 
      <input type="submit" value="Log in" /> 
     </form> 
    </lift:LogInForm.loggedOut> 

    <!-- User is logged in, show who she is and a way to log out using the method loggedIn --> 
    <lift:LogInForm.loggedIn> 
     <form class="lift:LogInForm.logOut?form=post"> 
     Connected as <span class="lift:LogInForm.getName" />.<br /> 
     <input type="submit" id="btnLogOut" value="Log out" /> 
     </form> 
    </lift:LogInForm.loggedIn> 
</div> 

3) ... y ahora el Scala/Lift lógica detrás de t su marcado:

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 } // Success: logged in 
      case _ => S.error("password", "Invalid username/password!") 
      } 
     } 
     case _ => S.error("name", "Invalid username format!") 
     } 
    } 

    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 
} 

Comentarios:

  • La selección entre las dos formas es hecho por la lógica, haciendo que sea el margen de beneficio proporcionado o NodeSeq.Empty, basado en el hecho de que el usuario es o bien inició sesión o cerró sesión.
  • Utilicé Levante: Msg para tener mensajes de error al lado de los campos apropiados (nombre/contraseña). El mensaje se envía usando S.error en la lógica detrás y la identificación apropiada.
  • De hecho, realizo las comprobaciones en un Validator helper usando regexps y formatos de verificación, etc. Esto devuelve un valor booleano cada vez que la coincidencia de patrones es trivial.
  • Uso el referer para que el usuario pueda iniciar/cerrar sesión mientras se encuentra en la misma página.
  • El nombre de usuario se mantiene como una variable sesión y se muestra cuando se inicia sesión en

4) Puede controlar el acceso a otras páginas haciendo lo siguiente en Boot.scala:.

def sitemap() = SiteMap(
     Menu("Home")/"index", 
     Menu("Protected page")/"protectedPageName" >> If(() => LogInForm.isLoggedIn, ""), 
     // etc. 
Preguntas:

  1. ninguna protección SSL (eso podría ser una mejora, aún no hemos visto esto en Scala/Lift). ¿Alguna experiencia de alguien más podría ser útil?
  2. Uso de variable de sesión. Tal vez hay una mejor manera de mantener el estado en Scala/Lift?
  3. ¿Ya hay algo hecho especialmente para ingresar/salir en Lift que podría haber pasado por alto?
  4. No es muy largo, pero podría ser más compacto? (Me gustaría no sacrificar demasiada legibilidad. Otros desarrolladores deben entenderlo rápidamente)
  5. ¿Alguna otra sugerencia?

Cheers,

Marc.

+1

No lo menciona pero ¿echó un vistazo al código ProtoUser/MegaProtoUser que está en Lift? – Debilski

Respuesta

2
  1. Esto muestra cómo forzar el uso de SSL, se suele usar que para el menú de inicio de sesión página de formulario: Lift filter to force ssl
  2. una variable de sesión es generalmente mejor manera de mantener la información de sesión
  3. protouser (se puede utilizar Lifty (http://lifty.github.com/) para configurar un proyecto con inicio de sesión como ejemplo)
2

Lift proporciona un marco de andamio para estas cajas de uso. Desea echarle un vistazo al código fuente de net.liftweb.proto.ProtoUser. Especialmente login() y loginXhtml.

Los ejemplos básicos de elevación hacen de la siguiente manera:

  1. Crear una propia clase de usuario asignada que utiliza todo el código de elevación desagradable

    package yourcompany.model 
    
    import net.liftweb.mapper._ 
    import net.liftweb.util._ 
    import net.liftweb.common._ 
    
    class User extends MegaProtoUser[User] { 
        // your code, mostly overrides 
    
    } 
    
    object User extends User with MetaMegaProtoUser[User] { 
        override def dbTableName = "users" 
        // other database related code 
    } 
    
  2. En el arranque después de que haya definido su mapa del sitio, haga lo siguiente:

    // do not forget to import your user 
    def sitemap() = Sitemap(Menu("Home")/"index" >> User.AddUserMenusAfter) 
    
    // now comes the magic 
    LiftRules.setSiteMap(User.sitemapMutator(sitemap())) 
    

Esto incluirá muchos enlaces en su página, todos reunidos bajo /user_mgt p. Ej. sign_up, login, lost_password. Para mí esta es una gran base para trabajar. Puede anular casi todas las cosas que suceden en ProtoUser, o simplemente copiar los buenos bits e implementar todo usted mismo.

Además, si alguien tiene documentos adicionales sobre eso, será de gran ayuda. Por el momento, intento descubrir la mayor parte del uso de las fuentes.

Cuestiones relacionadas