加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala:如何获得未来的结果

发布时间:2020-12-16 10:04:36 所属栏目:安全 来源:网络整理
导读:我有一个像这样返回Future的方法…… def isTokenExpired(token: String): Future[Boolean] = { ...} …然后我有另一个调用isTokenExpired的方法返回一个这样的布尔值: def isExpired(token: String): Boolean = { var result = true isTokenExpired(token)
我有一个像这样返回Future的方法……

def isTokenExpired(token: String): Future[Boolean] = {
  ...
}

…然后我有另一个调用isTokenExpired的方法返回一个这样的布尔值:

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.为了使事情变得更复杂,我在这个定制的Action中使用这个jwt:

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],如果我使用Future.map则会返回isExpired.你明白了吗?

解决方法

你写的函数不会像你想象的那样工作.它(可能)首先返回true,然后设置结果变量.

通常你会做这样的事情:

isTokenExpired(token).map { result =>
   // do stuff
}

在像Play这样的框架中,您可以将Future映射到http响应,并为Play回放一个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
    }
  }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读