【问题标题】:Play2.2.x, BodyParser, Authentication, and Future[Result]Play2.2.x、BodyParser、身份验证和未来[结果]
【发布时间】:2013-12-19 01:50:11
【问题描述】:

我正在尝试在我的 Play 2.2.1 应用中实现身份验证,但我不知道如何使用返回 Future[Result] 的操作使其工作。

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

Play 2.0 Framework, using a BodyParser with an authenticated request

如何让它与 Futures 一起使用? IE。我将如何实现这个功能:

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]。

那么,如何最好地构建整个东西?我需要能够制作依赖于异步数据库调用的身份验证包装器。

我确定我只是很密集并且缺少一些明显的东西......

编辑:这就是我最终的结果。感谢 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()))
      }
    }
  }
}

【问题讨论】:

  • 如果您尝试case None => Future.successful(onUnauthorized(request)) 会怎样?虽然,@Jean 的方法似乎更好。

标签: scala authentication playframework-2.2


【解决方案1】:

在 play 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)
  }
}

【讨论】:

  • 谢谢,让。我了解了 Play 的 ActionBuilder 的最新信息(尽管我还是个新手),这就是我最终得到的结果:
  • 看看安全社交如何实现其SecuredAction,特别是您会注意到他们将身份验证的实现与授权的实现分开,这是一件好事(IMO)。在您当前的实现中,您混合了这两个问题:)
猜你喜欢
  • 2013-11-03
  • 2015-09-23
  • 2014-11-07
  • 1970-01-01
  • 2015-02-02
  • 2018-02-24
  • 1970-01-01
  • 2016-05-16
  • 1970-01-01
相关资源
最近更新 更多