我正在尝试在我的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()))
}
}
}
}
答案 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)
}
}