【问题标题】:How to prevent percent decoding of URIs in Akka Http redirect requests?如何防止 Akka Http 重定向请求中的 URI 百分比解码?
【发布时间】:2020-10-16 15:02:11
【问题描述】:

我正在使用 Akka Http 处理存储在 GCS(Google 云存储)中的文本资源的签名 URL 的重定向。基本上,我在 GCS 中有一些文件,我想在请求时与我的 API 一起提供。不幸的是,该资源的路径有一个子目录,其中包含= 字符,例如:https://storage.googleapis.com/path_start/more_path/format=avro/still_more_path/filename.avro

Google 存储的signUrl 方法将这个= 字符编码为签名网址中的%3D。我已经验证使用这个签名的 url 允许访问 GCS 中的文件(点击它开始下载)。问题是当 Akka Http 根据请求重定向这个签名的 url 时,它会将其解码回等号。这会导致来自 GCS 的 SignatureDoesNotMatch 错误:


    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>

我尝试生成签名的 url 并在以下代码 sn-p 中重定向它:

          val signedUrl = cloudStorageService.getSignedUrl(bucketId, filePath)
          logger.info(s"signedUrl: $signedUrl")
          redirect(signedUrl, TemporaryRedirect)

我的cloudStorageService.getSignedUrl方法在这里单独定义:

      def getSignedUrl(bucket: String, path: String, expiration: Option[Int] = None): String = {
        val maxLifetime = 60 * 60 * 24 * 7
        val lifetime = expiration.getOrElse(maxLifetime)
        val cappedExpiration = Math.min(lifetime, maxLifetime)
    
        val blobInfo = BlobInfo.newBuilder(BlobId.of(bucket, path)).build
        storage.signUrl(blobInfo, cappedExpiration, TimeUnit.SECONDS, Storage.SignUrlOption.withV4Signature).toString
      }

第一个 sn-p 中的日志记录语句显示签名 URL 是使用 %3D 生成的,但重定向的 URL 已被百分比解码回 =,从而导致 SignatureDoesNotMatch 错误。

这种行为是 Akka Http 的标准行为。根据 Uri 类的the documentation,所有成员都代表解码元素的百分比。可以选择使用原始查询字符串构造 uri,但在我的情况下,编码字符发生在 uri 路径本身,而不是查询中。

所以我的问题是,有没有办法让 Akka Http 重定向这个签名的 url 而无需对路径进行百分比解码?还是我弄错了,还有其他方法可以解决这个问题?

【问题讨论】:

  • 我看到的一个建议是使用 Synthetic Header such as Raw-Request-URI 作为绕过 url 规范化的一种方式。但是,似乎不允许这些标头进行重定向。尝试添加此标头时,我收到一条警告,提示“响应中不允许使用原始请求 URI”。还有其他方法可以在重定向中利用此类标头吗?

标签: scala akka urlencode akka-http


【解决方案1】:

您可能希望“手动”创建重定向响应,而不是使用“重定向”指令。指令的实现是:

HttpResponse(
      status = redirectionType,
      headers = headers.Location(uri) :: Nil,
      entity = redirectionType.htmlTemplate match {
        case ""       => HttpEntity.Empty
        case template => HttpEntity(ContentTypes.`text/html(UTF-8)`, template format uri)
      }

您可以引入自己的RawHeader 来代替headers.Location(uri) 来构建Location 标头。您可以在场景中将实体留空。

(相关文档:https://doc.akka.io//docs/akka-http/current/routing-dsl/directives/route-directives/redirect.html

【讨论】:

  • 谢谢!我之前尝试过这个,但是使用headers.Location(uri) a la 文档而不是RawHeader("Location", uri),它有效
猜你喜欢
  • 2014-06-23
  • 1970-01-01
  • 2017-03-29
  • 2020-07-03
  • 1970-01-01
  • 2021-08-27
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
相关资源
最近更新 更多