簡體   English   中英

在 Scala Play Framework 中,是否有一種簡單的方法來驗證表單請求上的簽名

[英]In Scala Play Framework, is there a simple way to verify signatures on form requests

我正在嘗試編寫一個 Scala Play Framework 操作,該操作將在包含表單 url 編碼數據的傳入 POST 請求上驗證 HmacSHA256 簽名。

這在 Play 框架中看起來並不簡單,因為:i) 動作構建器只能訪問標頭,但不能訪問請求主體,以及 ii) 為了計算簽名,我們必須將請求主體視為Array[ByteString] ,但是當我們開始處理表單數據時,我們必須將其視為Map[String, Seq[String]] ,問題是 Play 迫使我們為請求選擇單一類型,我們不能輕易地“投射”請求主體為不同的類型。

我能夠想出的唯一解決方案是使用ActionRefiner ,它返回一個嵌入回調的WrappedRequest來驗證簽名。 回調依次使用FormUrlEncodedParser.parse(new String(request.body.toArray))解析數據。 下面的代碼說明了這種方法。

這一切似乎過於復雜。 有沒有更簡單的方法來驗證 Play 中的 Hmac 簽名,或者我只是遇到了 API 的限制?

package actions

import akka.util.ByteString
import com.google.inject.Inject
import play.api.Logging
import play.api.mvc.Results.Unauthorized
import play.api.mvc._
import play.core.parsers.FormUrlEncodedParser
import services.SlackSignatureVerifierService

import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try

class SlackRequest[A](
    val validateSignature: String => Try[String],
    request: Request[A]
) extends WrappedRequest[A](request)

object SlackSignatureVerifyAction {

  implicit class SlackRequestByteStringValidator(
      slackRequest: SlackRequest[ByteString]
  ) {
    def validateSignatureAgainstBody(): Try[Map[String, Seq[String]]] = {
      val raw = slackRequest.body.utf8String
      slackRequest.validateSignature(raw) map { _ =>
        FormUrlEncodedParser.parse(new String(slackRequest.body.toArray))
      }
    }
  }

  val HEADERS_TIMESTAMP: String = "X-Slack-Request-Timestamp"
  val HEADERS_SIGNATURE: String = "X-Slack-Signature"
}

class SlackSignatureVerifyAction @Inject() (
    val parser: BodyParsers.Default,
    slackSignatureVerifierService: SlackSignatureVerifierService
)(implicit ec: ExecutionContext)
    extends ActionBuilder[SlackRequest, AnyContent]
    with ActionRefiner[Request, SlackRequest]
    with Logging {

  override protected def executionContext: ExecutionContext = ec

  override protected def refine[A](
      request: Request[A]
  ): Future[Either[Result, SlackRequest[A]]] = {

    val timestamp =
      request.headers.get(SlackSignatureVerifyAction.HEADERS_TIMESTAMP)

    val signature =
      request.headers.get(SlackSignatureVerifyAction.HEADERS_SIGNATURE)

    (timestamp, signature) match {
      case (Some(timestamp), Some(signature)) =>
        Future.successful {
          val validate = (body: String) =>
            slackSignatureVerifierService.validate(timestamp, body, signature)
          Right(new SlackRequest[A](validate, request))
        }
      case _ =>
        Future { Left(Unauthorized("Invalid signature headers")) }
    }

  }

}

你是對的,沒有一種簡單的方法可以在 Play 上驗證 Hmac 簽名! 項目。 最后,您的方法似乎有一個非常合理的實現,並且可以更容易地適應其他使用 Hmac 簽名的提供商,例如 GitHub 和 Stripe。

我真的認為它可能是一個很好的開源項目,它可以提供一個帶有包裝RequestAction ,甚至是一個帶有為其他提供者進行自定義簽名驗證的方法的Service 為什么不通過 GitHub 的開源項目幫助 Play 社區?

我創建了一個新的 Play 模塊來驗證 Hmac 簽名。 詳細信息可以在這里找到:

https://github.com/phelps-sg/play-hmac-signatures

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM