如何防止 Akka Http 重定向请求中 URI 的百分比解码?

How to prevent percent decoding of URIs in Akka Http redirect requests?

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

Google 存储的 signUrl 方法百分比将此 = 字符编码为签名 url 中的 %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 并在以下代码片段中重定向它:

          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
      }

第一个片段中的日志记录语句显示带符号的 URL 是使用 %3D 生成的,但是重定向的 URL 已被百分比解码回 =,导致 SignatureDoesNotMatch 错误。

此行为是 Akka Http 的标准行为。根据 the documentation for Uri class,所有成员都表示已解码元素的百分比。可以选择使用原始查询字符串构造 uri,但在我的例子中,编码字符出现在 uri 路径本身,而不是查询中。

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

您可能想要创建重定向响应 'manually' 而不是使用 'redirect' 指令。指令实现为:

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 header。您可以在您的方案中将实体留空。

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