AJAX上传期间的Amazon S3 SignatureDoesNotMatch

时间:2013-05-07 01:48:36

标签: scala amazon-s3 base64

不得不将AJAX上传到S3,但请求将返回SignatureDoesNotMatch。在仔细检查访问密钥ID和密钥后,我对此问题感到茫然。我可以确认我确实也设置了CORS策略文件。

这是AJAX请求:

$.ajax({
              url: 'https://my-bucket.s3.amazonaws.com/',
              type: 'POST',
              contentType: false,
              data: formData,
              success: function() {
                self.unblockUI($('#body'));
              },
              error: function() {
                self.unblockUI($('#body'));
              },
              cache: false,
              processData: false
            });

在Scala中执行我的策略编码/签名。这是代码:

  val policy = """
    |{"expiration": "2020-01-01T00:00:00Z",
    |"conditions": [ 
    |{"bucket": "my-bucket"}, 
    |["starts-with", "$key", "uploads/"],
    |{"acl": "public-read"},
    |{"success_action_redirect": "http://example.com/"},
    |["starts-with", "$Content-Type", ""],
    |["starts-with","$Filename",""],
    |["content-length-range", 0, 5242880]
    |]

        |}
        """.stripMargin.stripLineEnd.replaceAll("\n", "").replaceAll("\r","")

  val policyEncoded = (new BASE64Encoder()).encode(policy.getBytes("UTF-8")).stripLineEnd.replaceAll("\n", "").replaceAll("\r","")

  val hmac = Mac.getInstance("HmacSHA1")
  hmac.init(new SecretKeySpec(AWS_SECRET.getBytes("UTF-8"), "HmacSHA1"))

  val policySignature = (new BASE64Encoder()).encode(hmac.doFinal(policy.getBytes("UTF-8"))).stripLineEnd.replaceAll("\n", "").replaceAll("\r","")

发送的表单如下:

------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="key"

uploads/reward/img/example_02820838f08sd083k.jpeg
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="AWSAccessKeyId"

<redacted>
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="acl"

public-read
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="success_action_redirect"

http://example.com/
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="policy"

eyJleHBpcmF0aW9uIjogIjIwMjAtMDEtMDFUMDA6MDA6MDBaIiwiY29uZGl0aW9ucyI6IFsgeyJidWNrZXQiOiAicGxhdGZvcm0zLWNsaWVudC1pbWFnZXMifSwgWyJzdGFydHMtd2l0aCIsICIka2V5IiwgInVwbG9hZHMvIl0seyJhY2wiOiAicHVibGljLXJlYWQifSx7InN1Y2Nlc3NfYWN0aW9uX3JlZGlyZWN0IjogImh0dHA6Ly8za3Vkb3MuY29tLyJ9LFsic3RhcnRzLXdpdGgiLCAiJENvbnRlbnQtVHlwZSIsICIiXSxbInN0YXJ0cy13aXRoIiwiJEZpbGVuYW1lIiwiIl0sWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDAsIDUyNDI4ODBdXX0gICAg
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="signature"

9jj1hW8pGpS32Ka4KA2R0MwYKTQ=
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="Content-Type"

image/jpeg
------WebKitFormBoundaryTNKMpdRiJxhC39QF
Content-Disposition: form-data; name="file"; filename="youtube_bg.jpg"
Content-Type: image/jpeg


------WebKitFormBoundaryTNKMpdRiJxhC39QF--

关于为什么失败的任何想法?我想我已经筋疲力尽了已知的可能性。谢谢!

1 个答案:

答案 0 :(得分:4)

签名有一些问题,但其中一个是我没有编码正确的字符串。为了帮助不希望使用Amazon SDK的未来Scala用户,这里是我的Scala S3Policy对象,它可以生成正确的签名。

注意:您不需要使用Play!记录器,除非您使用Play!框架;)

import play.api.Logger

import sun.misc.BASE64Encoder
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import com.codahale.jerkson.Json._

object S3Policy {

  val AWS_ACCESS_KEY_ID = "<your access key ID goes here>"
  val AWS_SECRET = "<your secret goes here>"
  val AWS_ALGO = "HmacSHA1"

  val policy = {
    val policyMap = Map(
      "expiration" -> "2014-01-01T12:00:00.000Z'",
      "conditions" -> List(
        Map("bucket" -> "<your bucket goes here>"),
        Map("success_action_status" -> "201"),
        Map("acl" -> "public-read"),
        Array("starts-with", "$key", "uploads/"),
        Array("starts-with", "$Content-Type", ""),
        Array("starts-with", "$x-amz-meta-clientid", ""),
        List("content-length-range", 0, 5242880)
      )
    )
    val generated = generate(policyMap)
    Logger.debug("AWS S3 POLICY: "+generated)
    generated
  }

  val policyEncoded = (new BASE64Encoder()).encode(policy.getBytes("UTF-8")).replaceAll("\n", "").replaceAll("\r","")

  val policySignature = signAndBase64Encode(policyEncoded, AWS_ALGO)

  /**
   * method to sign an AWS request
   */
  def signAndBase64Encode(stringToSign: String, algo: String) = {
    try {
      val mac = Mac.getInstance(algo)
      mac.init(new SecretKeySpec(AWS_SECRET.getBytes("UTF-8"), algo))
      val signature = mac.doFinal(stringToSign.getBytes("UTF-8"))
      val encoded = (new BASE64Encoder()).encode(signature)

      Logger.debug("AWS S3 SIGNATURE: "+encoded)
      encoded

    } catch {
      case e => Logger.error("Unable to calculate a request signature: " + e.getMessage(), e); "ERRORSTRINGNOTCALCULATED"
    }
  }

}

在上述政策中,您会注意到我使用的是x-amz-meta-字段。如果您使用的是S3 metas,则需要以这种或那种方式定义。