Play2.2.x,BodyParser,身份验证和未来[结果]

时间:2013-12-19 01:50:11

标签: scala authentication playframework-2.2

我正在尝试在我的Play 2.2.1应用中实现身份验证,而我无法弄清楚如何使其与返回Future [Result]的操作一起使用。

这篇文章描述的非常接近我正在尝试做的事情,除非没有返回Future [Result]:

Play 2.0 Framework, using a BodyParser with an authenticated request

如何让它与Futures合作?即我将如何实现这个功能:

def IsAuthenticated(f: => String => Request[Any] => Future[Result])

或者更好的是,这个功能:

def IsAuthenticated[A}(b:BodyParser[A])(f: => String => Request[Any] => Future[Result])

将提供此功能:

def AuthenticatedUser(g:Account => Request [AnyContent] => SimpleResult)= IsAuthenticated {...}

在我的控制器中包装异步操作?

这部分我可以做:

  def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) = {
    Security.Authenticated(email, onUnauthorized) {
      user => Action.async(request => f(user)(request))
    }
  }

但是如果我尝试在我的包装函数中使用IsAuthenticated:

  def AuthenticatedUser(g: Account => Request[AnyContent] => Future[SimpleResult]) =     IsAuthenticated {
    email => implicit request => Account.find(email).map {
      opt => opt match {
        case Some(account) => g(account)(request)
        case None => Future(onUnauthorized(request))
      }
    }
  }

(Account.find返回一个Future [Option [Account]]'因为这是一个可能需要一些时间的mongodb调用。做未来事情的愿望正是让我如此悲伤的原因)

我无法使AuthenticatedUser满足编译器。它说它正在获得Future [Future [SimpleResult]]而不是Future [SimpleResult]。

那么,如何最好地构建这整个事物?我需要能够创建依赖于异步db调用的身份验证包装器。

我确信我只是密集而且缺少明显的东西......

编辑:这就是我最终的结果。谢谢Jean让我指出了正确的方向。

我发现AuthenticatedController在生根周围时,它非常接近我正在寻找的东西。我想要两种类型的身份验证:用户(经过身份验证的用户)和管理员(用于包装管理任务的代码)。

package controllers

import models.Account

import play.api.mvc._
import scala.concurrent.Future

trait Secured {

  class AuthenticatedRequest[A](val account: Account, request: Request[A]) extends WrappedRequest[A](request)

  object User extends ActionBuilder[AuthenticatedRequest] {
    def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
      request.session.get("email") match {
        case Some(email) => {
          Account.find(email).flatMap {
            case Some(account) => {
              block(new AuthenticatedRequest(account, request))
            }
            case _ => Future(Results.Redirect(routes.Index.index()))
          }
        }
        case _ => Future(Results.Redirect(routes.Index.index()))
      }
    }
  }

  object Administrator extends ActionBuilder[AuthenticatedRequest] {
    def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
      request.session.get("email") match {
        case Some(email) => {
          Account.find(email).flatMap {
            case Some(account) => if (account.admin) {
              block(new AuthenticatedRequest(account, request))
            } else {
              Future(Results.Redirect(routes.Index.index()))
            }
            case _ => Future(Results.Redirect(routes.Index.index()))
          }
        }
        case _ => Future(Results.Redirect(routes.Index.index()))
      }
    }
  }
}

1 个答案:

答案 0 :(得分:4)

播放2.2中有一些变化,以便更容易编写动作。您引用的资源已过时。

相反,你应该通过扩展ActionBuilder创建一个自定义动作构建器来创建你的动作,这将获得你可能需要的所有花哨的应用方法(包括异步支持和所有)

例如,您可以这样做:

trait MyAction extends Results{

  class MyActionBuilder[A] extends ActionBuilder[({ type R[A] = Request[A] })#R] {
    def invokeBlock[A](request: Request[A], 
                       block: Request[A] => Future[SimpleResult]) ={
      // your authentication code goes here :
      request.cookies.get("loggedIn").map { _=>
        block(request)
      } getOrElse Future.successful(Unauthorized)
    }
  }

  object MyAction extends MyActionBuilder
}

然后你可以这样使用:

object MyController extends Controller with MyAction{
  def authenticatedAction=MyAction {
   Ok
  }
  def asyncAuthenticatedAction=MyAction.async {
    Future.successful(Ok)
  }
  def authenticatedActionWithBodyParser = MyAction(parse.json){ request =>
    Ok(request.body)
  }
}

为了简洁起见,我使用了一个非常简单的身份验证机制,你想要改变它:)

此外,您还可以创建自定义"请求"键入以提供其他信息。例如,您可以定义AuthenticatedRequest:

  case class AuthenticatedRequest[A](user: User, request: Request[A]) extends WrappedRequest(request)

如果您有办法让您的用户

  object User{
    def find(s:String): Option[User] = ???
  }

然后稍微更改构建器定义

  class MyActionBuilder[A] extends 
        ActionBuilder[({ type R[A] = AuthenticatedRequest[A] })#R] {
    def invokeBlock[A](request: Request[A], 
                       block: AuthenticatedRequest[A] => Future[SimpleResult]) ={
      // your authentication code goes here :
      (for{
        userId <- request.cookies.get("userId")
        user <- User.find(userId.value)
      }yield {
        block(AuthenticatedRequest(user,request))
      }) getOrElse Future.successful(Unauthorized)
    }
  }

您的控制器现在可以通过authenticatedActions访问您的用户:

object MyController extends Controller with MyAction{
  val logger = Logger("application.controllers.MyController")
  def authenticatedAction=MyAction { authenticatedRequest =>
    val user = authenticatedRequest.user
   logger.info(s"User(${user.id} is accessing the authenticatedAction")  
   Ok(user.id)
  }
  def asyncAuthenticatedAction = MyAction.async { authenticatedRequest=>
    Future.successful(Ok(authenticatedRequest.user.id))
  }
  def authenticatedActionWithBodyParser = MyAction(parse.json){ authenticatedRequest =>
    Ok(authenticatedRequest.body)
  }
}