Лучший способ создать форму входа / выхода в Scala с помощью Lift

Поскольку все больше и больше людей интересуются Scala (как и я), а не вопросом, я хотел бы обсудить одну реализацию фрагмента кода входа / выхода для веб-приложения на основе на Lift .

Я только начал изучать Scala и Lift, так что, вероятно, не лучший способ реализовать такую ​​функцию, но я хотел бы поделиться им с другими новичками и обсудить с более опытные разработчики. Please note that I'm also not an expert in web development. Any help for improvements would be greatly appreciated (especially performance and security related ones) ;-)

1) First of all, the snippet needs to be easily plugable, like with 1 line of code in your default template. I've done it using the embedded Lift feature (notice the underscore so it can't be rendered as a page itself but only invoked from a rendered page, in short, some kind of "private" snippet):

<lift:embed what="_logInForm" />

2) Then, in _logInForm.html, I use the below markup and a conditional display to handle everything:

<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) ... and now the Scala/Lift logic behind this markup:

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
}

Comments:

  • The selection between the two forms is made by the logic, rendering either the provided markup or NodeSeq.Empty, based on the fact the user is either logged in or logged out.
  • I used Lift:Msg to have error messages next to the appropriate fields (name/password). The message is sent using S.error in the logic behind and the appropriate id.
  • I actually perform the checks in a Validator helper using regexps and formats checks, etc. This return a boolean each time to pattern matching is trivial.
  • I use the referer so the user can log in / log out while staying on the same page.
  • The username is kept as a session variable and shown when logged in.

4) You can control access to other pages doing the following in Boot.scala:

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

Questions:

  1. No SSL protection (that could be an improvement, haven't had a look at this in Scala/Lift yet). Any experience from someone else could be useful?
  2. Use of session variable. Maybe there is a better way to keep state in Scala/Lift?
  3. Is there already something made especially for logging in/out in Lift that I could have missed?
  4. It's not very long, but could maybe be more compact? (I'd like not to sacrifice too much readability though. Other developers need to quickly understand it)
  5. Any other suggestion?

Cheers,

Marc.

5
задан pnuts 5 February 2015 в 20:23
поделиться