问题
I'm trying to implement an authentication mechanism similar to this example:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("X-SECRET-TOKEN")
maybeToken map { token =>
action(token)(requestHeader) // apply requestHeader to EssentialAction produces the Iteratee[Array[Byte], SimpleResult]
} getOrElse {
Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
}
}
However, in my case I'm mapping a random token value to a session on the server side stored in Mongodb. The goal was to be able to let a user terminate all his other sessions at will.
However, the data I get from ReactiveMongo is going to be wrapped in a Future.
I would like something like this:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("session")
maybeToken map { token =>
//This returns a future..
Session.find(session).map { result =>
result match
case Some(session) => action(session)(requestHeader)
case None => Done(Unauthorized())
}
} getOrElse {
Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
}
}
Is this possible with EssentialAction?
回答1:
Iteratee.flatten
goes from Future[Iteratee[A, E]] => Iteratee[A, E]
so you could do it like this:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("session")
val futureIteratee: Future[Iteratee[Array[Byte], SimpleResult]] = maybeToken map { token =>
//This returns a future..
Session.find(token).map {
case Some(session) => action(session)(requestHeader)
case None => Done[Array[Byte], SimpleResult](Unauthorized("Invalid token"))
}
} getOrElse {
Future.successful(Done[Array[Byte], SimpleResult](Unauthorized("401 No Security Token\n")))
}
Iteratee.flatten(futureIteratee)
}
回答2:
You can use an ActionBuilder as the invokeBlock method return a Future[SimpleResult] so you can flatMap your future into a call to the underlying block
Something like
object Authenticated extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
Session.find(session).map { result =>
result match
case Some(session) => block(new AuthenticatedRequest(session))
case None => Unauthorized()
}
}
}
where AuthenticatedRequest is your request type that wraps your session object
来源:https://stackoverflow.com/questions/21867782/play-2-2-essentialaction-with-futures