我有一个像这样返回Future
的方法......
def isTokenExpired(token: String): Future[Boolean] = {
...
}
...然后我又调用isTokenExpired
的另一种方法,它返回Boolean
,如下所示:
def isExpired(token: String): Boolean = {
var result = true
isTokenExpired(token).onComplete {
case Success(r) => result = r
case Failure(_) => result = true
}
result
}
有没有更好的方法来编写isExpired
方法?
修改
根据EECOLOR的要求,让我向您提供更多详细信息。对于我的Play应用程序,我实现了基于JSON Web Token(jwt)的授权机制。所有声明都包含在jwt中,但到期时间除外,它存储在MongoDB集合中。以下是我的Token
课程的总结:
class Token {
...
def id: String = { ... }
def issueTime: LocalDateTime = { ... }
def issuer: String = { ... }
...
def isValid: Boolean = { ... }
def isExpired: Boolean = { /* uses ReactiveMongo to access MongoDB */ }
}
如您所见,除了到期信息之外,所有jwt属性都是自包含的。方法isExpired
使用ReactiveMongo,它始终返回Future
。为了使事情变得更复杂,我将这个jwt用于这样的自定义Action
:
class SecuredAction[T <: Controller] private(private val methodName: String)
extends ActionBuilder[ApiRequest] {
...
def invokeBlock[A](request: Request[A], block: (ApiRequest[A]) => Future[SimpleResult]) = {{
request.headers.get(HeaderNames.AUTHORIZATION) match {
case Some(header) => s"""$AuthType (.*)""".r.unapplySeq(header).map(_.head.trim)
case _ => None
}} match {
case Some(tokenString) => {
val token = Token(tokenString)
if (!token.isValid) {
Logger.warn(s"request ${request.uri} not authorized: token ${token.id} has been tampered")
Future.successful(Unauthorized(AuthErrors.authenticationViolated(token.subject)(request).asJson))
} else if (token.isExpired) {
Logger.debug(s"request ${request.uri} not authorized: token ${token.id} has expired")
Future.successful(Unauthorized(AuthErrors.authenticationExpired(token.subject)(request).asJson))
} else if (!isAuthorized(token)) {
Logger.info(s"request ${request.uri} not authorized: required claims not defined for account ${token.subject}")
Future.successful(Forbidden(AuthErrors.requestNotAuthorized(token.subject)(request).asJson))
} else {
Logger.debug(s"request ${request.uri} authorized for account ${token.subject}")
block(new ApiRequest(token, request))
}
}
case _ => {
Logger.debug(s"request ${request.uri} not authenticated")
Future.successful(Unauthorized(
AuthErrors.requestNotAuthenticated()(request).asJson
).withHeaders(HeaderNames.WWW_AUTHENTICATE -> AuthType))
}
}
}
正如您所看到的,如果我使用Future[play.mvc.results.Result]
,我需要返回Future[Boolean]
,而不是返回isExpired
Future.map
。你明白了吗?
答案 0 :(得分:6)
你写的函数不会像你想象的那样工作。它(可能)首先返回true
,然后设置result
变量。
通常你会做这样的事情:
isTokenExpired(token).map { result =>
// do stuff
}
在像Play这样的框架中,您可以将Future
映射到http响应,然后播放一个Future[SimpleResult]
。 Play知道如何处理Future
结果。
一般情况下,建议您不要等待Future
在生产代码中完成,而是使用Future
中的值并让您使用的框架处理结果
在测试中,等待结果可能会派上用场,你可以这样做:
Await.result(someFuture, 5.seconds)
修改强>
我可能会提取令牌的构造,以便最终得到Future[Token]
。这让我更容易创作。它还允许我创建具有更好架构并且更容易测试的代码。
我可能会将代码分解为更小的方法,但下面的示例让您了解我将采取的方向。
class TokenService(connection: MongoConnection) {
def tokenFor(tokenString: String): Future[Token] = ???
}
class SecuredAction(tokenService: TokenService) extends
ActionBuilder[ApiRequest] {
import play.api.libs.concurrent.Execution.Implicits._
def invokeBlock[A](request: Request[A], block: (ApiRequest[A]) => Future[SimpleResult]) =
extractTokenFrom(request) match {
case Some(tokenString) => {
tokenService.tokenFor(tokenString) flatMap {
case token if (!token.isValid) =>
Logger.warn(s"request ${request.uri} not authorized: token ${token.id} has been tampered")
Future.successful(Unauthorized(AuthErrors.authenticationViolated(token.subject)(request).asJson))
case token if (token.isExpired) =>
Logger.debug(s"request ${request.uri} not authorized: token ${token.id} has expired")
Future.successful(Unauthorized(AuthErrors.authenticationExpired(token.subject)(request).asJson))
case token if (!token.isAuthorized) =>
Logger.info(s"request ${request.uri} not authorized: required claims not defined for account ${token.subject}")
Future.successful(Forbidden(AuthErrors.requestNotAuthorized(token.subject)(request).asJson))
case token =>
Logger.debug(s"request ${request.uri} authorized for account ${token.subject}")
block(new ApiRequest(token, request))
}
}
case _ =>
Logger.debug(s"request ${request.uri} not authenticated")
Future.successful(Unauthorized(
AuthErrors.requestNotAuthenticated()(request).asJson).withHeaders(HeaderNames.WWW_AUTHENTICATE -> AuthType))
}
val AuthType = "MyAuthType"
val TokenHeader = s"""$AuthType (.*)""".r
def extractTokenFrom(request: RequestHeader) = {
val authorizationHeader = request.headers.get(HeaderNames.AUTHORIZATION)
authorizationHeader flatMap {
case TokenHeader(token) => Some(token.trim)
case _ => None
}
}
}
答案 1 :(得分:0)
一种解决方案是通过字面来永久等待结果:
Await.result(futureResult, Duration.Inf)
等待永远将Future[Value]
转换为您想要的Value
。
我喜欢在我的代码中评论这一举动...
// wait forever to make future current (?)
Obs。我的ScalaTest中的无限期仅。对于生产,等待期应该按照@EECOLOR进行调整。