使用Lift在Scala中创建登录/注销表单的最佳方式



随着越来越多的人(像我一样)对Scala感兴趣,而不是一个问题,我想讨论一个基于Lift的网络应用程序登录/注销片段的实现。

我刚开始学习Scala和Lift,所以这可能不是实现此类功能的最佳方式,但我想与其他初学者分享,并与更有经验的开发人员讨论。请注意,我也不是网络开发方面的专家。我们将非常感谢对改进的任何帮助(尤其是性能和安全相关的帮助);-)

1)首先,片段需要易于插入,就像默认模板中的1行代码一样。我使用嵌入式Lift功能完成了这项工作(注意下划线,这样它就不能被呈现为页面本身,只能从呈现的页面中调用,简而言之,是某种"私人"片段):

<lift:embed what="_logInForm" />

2)然后,在_logInForm.html中,我使用以下标记和条件显示来处理所有内容:

<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)。。。现在是这个标记背后的Scala/Lift逻辑:

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
}

评论:

  • 两种形式之间的选择由逻辑进行,根据用户登录或注销的事实,呈现所提供的标记或NodeSeq.Empty
  • 我使用Lift:Msg在相应的字段(名称/密码)旁边显示错误消息。消息是使用S.error后面的逻辑和相应的id发送的
  • 实际上,我使用regexp和格式检查等在Validator助手中执行检查。每次返回布尔值到模式匹配都很简单
  • 我使用referer,这样用户就可以在同一页面上登录/注销
  • 用户名保留为会话变量,并在登录时显示

4)您可以在Boot.scala中控制对其他页面的访问:

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

问题:

  1. 没有SSL保护(这可能是一个改进,还没有在Scala/Lift中看到这一点)。其他人的经验可能有用吗
  2. 会话变量的使用。也许在Scala/Lift中有更好的方法来保持状态
  3. 是否已经有我可能错过的专门为登录/注销而制作的东西
  4. 它不是很长,但可能更紧凑?(不过,我不想牺牲太多的可读性。其他开发人员需要快速理解它)
  5. 还有其他建议吗

干杯,

马克。

  1. 这显示了如何强制使用SSL,您通常会将其用于登录表单页面菜单:Lift filter以强制使用SSL
  2. 会话变量通常是保存会话信息的最佳方式
  3. ProtoUser(你可以使用Lifty(http://lifty.github.com/)以登录为例设置项目)

Lift为这些用例提供了一个脚手架框架。您需要了解net.liftweb.proto.ProtoUser的源代码。尤其是CCD_ 2和CCD_。

Lift的基本示例如下所示:

  1. 创建一个自己的映射用户类,使用所有讨厌的提升代码

    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. 在您定义网站地图后的引导中,请执行以下操作:

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

这将包括您页面上的许多链接,所有链接都收集在/user_mgt下,例如sign_up, login, lost_password。对我来说,这是一个很好的工作基地。你可以覆盖ProtoUser中发生的几乎所有事情,或者只是复制好的部分并自己实现所有事情。

此外,如果有人有关于这方面的额外文档,这将非常有帮助。目前,我试图利用这些来源找出大部分。

最新更新