[英]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。
我真的認為它可能是一個很好的開源項目,它可以提供一個帶有包裝Request
的Action
,甚至是一個帶有為其他提供者進行自定義簽名驗證的方法的Service
。 為什么不通過 GitHub 的開源項目幫助 Play 社區?
我創建了一個新的 Play 模塊來驗證 Hmac 簽名。 詳細信息可以在這里找到:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.