EssentialAction:如何在不解析请求的情况下获取请求体

4
鉴于以下的 EssentialAction...
object MyController extends Controller {

  ...

  def HasToken(action: Token => EssentialAction) = EssentialAction { request =>

    ...

    // this doesn't compile
    val body = request.body match {
      case json: JsValue => json.toString
      case _ => ""
    }

    // calculate hash with body content here
    ...
  }

  // here is an authenticated action
  def getUser(userId: Strign) = HasToken { token =>
    Action(parse.json) { request =>
      request.body.validate[User] match {
        ...
      }
    }
  }
}

如何在不解析请求的情况下获取请求正文内容?

我不想也不需要在HasToken中解析请求正文,因为请求正文将在getUser操作中被解析。 我只需要请求正文的原始内容来计算哈希值。

HasToken中的代码无法编译,因为requestRequestHeader类型,而我需要一个定义了bodyRequest


HasToken的职责不清楚。hasToken逻辑在哪里?我只看到你试图访问主体来计算有效载荷哈希值。同时也不清楚你想要什么。你想要编译吗?如果是这样,我必须问一下为什么你的HasToken签名是Token => EssentialAction而不是Token => Request。如果你想让HasToken不解析主体,那么为什么不在RequestHeader中发送令牌,如果这是你想要的话? - Sudheer Aedama
HasToken 验证请求头是否具有有效的 JSON Web Token... 并使用哈希验证请求未被篡改。无论如何,这些细节对我的问题毫无用处。我只是想知道如何获取请求的原始内容(我需要它来计算哈希值,该哈希值必须与令牌中包含的哈希值相对应)。 - j3d
我不这么认为,如果您接受我的答案的话 :) - Sudheer Aedama
1个回答

3
这对您有效吗?
object MyController extends Controller {

  // Your HasToken Action
  def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
    // ... execute logic to verify authenticity using requestHeader
  }

  // Your action to validate tampering of request body and validity of JSON
  def Validate[A](action: Token => Request[A]) = Action(parse.json) { request =>
    val body = request.body
    body match {
      case json: JsValue => json.toString
      case _ => ""
    }
    // calculate hash with body content here

    body.validate[User] match {
      // ...
    }
  }

  def getUser(userId: Strign) = Authenticate { token =>
    Validate { user =>
      //.... Continue
    }
  }
}
  • 身份验证仅使用RequestHeader
  • 验证使用请求正文。(奖励:正文只解析一次)

编辑:

问题#1: 我不想在Validate中验证正文..因为我需要一个通用的验证机制,无论内容类型如何都可以在任何地方使用(例如用户,消息等)。

怎么样再添加另一个类型参数(使其变成通用)?

def Validate[A, B](action: Token => Request[A])(implicit reads: Reads[B]) = Action(parse.json) { request =>
   // ...
}
问题 #2: 此外,如果令牌验证失败,则不必处理正文(在文件上传的情况下尤为重要,只有在验证成功时才需要执行)。因此,我认为最好的选择是在“验证”中读取正文的原始内容。
这可以很容易地实现:
def Validate[A, B](action: Token => Request[A])(implicit reads: Reads[B]) = Action(parse.json) { request =>
   val body = request.body
    body match {
      case json: JsValue => json.toString
      case _ => ""
    }
    // calculate hash with body content here and figure out if the body is tampered
    if (bodyIsNotTampered) {
      body.validate[B] match {
        // ...
      }
    } else {
      // log and return Future.successful(BadRequest)
    }
  }

编辑3:完整解决方案:

import play.api.libs.json.{Json, JsValue, Format}

object CompilationUtils {
  class Token
  case class User(name: String)
  implicit val UserFormat = Json.format[User]

  def authenticate = new Token // authentication logic

  def isTampered(body: JsValue) = {
    val bodyAsStr: String = Json.stringify(body)
    // calculate hash with body content here
    false
  }

}

object MyController extends Controller {
  import CompilationUtils._

  // Your HasToken Action
  def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
    action(authenticate)(requestHeader) // your execute logic to verify authenticity using requestHeader
  }

  // Your action to validate tampering of request body and validity of JSON
  def Validate[A, B](request: Request[A])(implicit formatA: Format[A], formatB: Format[B]): Either[Result, B] = {
    val body = request.body
    val bodyAsJsValue = Json.toJson(body)
    if (!isTampered(bodyAsJsValue)) {
      bodyAsJsValue.validate[B].fold(
        valid   = res => Right(res),
        invalid = err => Left(BadRequest(err.toString))
      )
    } else {
      Left(BadRequest) // Request Tampered
    }
  }

  def getUser(userId: String) = Authenticate { token =>
    Action(parse.json) { request =>
      Validate(request).fold(
        badReq => badReq,
        user   =>
          // continue...
          Ok("")
      )
    }
  }
}

谢谢你的回答。我不想在“验证”中验证主体...因为我需要一个通用的验证机制,无论内容类型如何都可以使用(例如用户、消息等)。此外,如果令牌验证失败,则不必处理正文(这在文件上传的情况下非常重要,只有在验证成功时才能执行上传操作)。因此,在我看来,最好的选择是在“验证”中读取主体的原始内容。 - j3d
@j3d:我已经回复了您的评论,请查看更新后的答案。 - Sudheer Aedama
问题在于解析器可能会根据请求而异。例如,如果请求体包含JSON,则使用parse.Json,但是在multipart/form-data(即文件上传)的情况下,解析器将是fsBodyParser,这是一个自定义解析器,直接将传入数据存储到MongoDB中。 - j3d
@j3d:将BodyParser也传递给Validate Action有什么问题吗? - Sudheer Aedama
1
Venkat,非常感谢您的大力支持;-) - j3d
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接