【问题标题】:Access Azure Storage Services REST API with Elixir and HTTPoison使用 Elixir 和 HTTPoison 访问 Azure 存储服务 REST API
【发布时间】:2018-01-10 08:25:09
【问题描述】:

我正在尝试使用 Elixir 访问 Azure Storage Services via their REST API,但我无法让 Authentication Header 工作。如果我使用 ex_azure 包(erlazure 的包装器),我可以连接,但当我尝试构建请求并使用 HTTPoison 时无法连接。

最近的错误消息

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Error>
  <Code>AuthenticationFailed</Code>
  <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:00000000-0000-0000-0000-000000000000\nTime:2017-08-02T21:46:08.6488342Z</Message>
  <AuthenticationErrorDetail>The MAC signature found in the HTTP request '<signature>' is not the same as any computed signature. Server used following string to sign: 'GET\n\n\nWed, 02 Aug 2017 21:46:08
    GMT\nx-ms-date-h:Wed, 02 Aug 2017 21:46:08 GMT\nx-ms-version-h:2017-05-10\n/storage_name/container_name?comp=list'.</AuthenticationErrorDetail>
</Error>

第一次编辑后

  <?xml version=\"1.0\" encoding=\"utf-8\"?>
  <Error>
    <Code>AuthenticationFailed</Code>
    <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:00000000-0000-0000-0000-000000000000\nTime:2017-08-03T03:03:57.1385277Z</Message>
    <AuthenticationErrorDetail>The MAC signature found in the HTTP request '<signature>' is not the same as any computed signature. Server used following string to sign: 'GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Thu, 03 Aug
      2017 03:03:57 GMT\nx-ms-version:2017-04-17\n/storage_name/container_name\ncomp:list\nrestype:container'.</AuthenticationErrorDetail>
  </Error>

依赖关系

# mix.exs
defp deps do
  {:httpoison, "~> 0.12"}
  {:timex, "~> 3.1"}
end

代码

  • 我是否正在格式化身份验证标头 (string_to_sign) 对吗?
  • 我使用的是编码/解码吗?
  • 我是否将标头正确添加到 HTTPoison?
  • 我应该为 REST 操作使用其他东西而不是 HTTPoison 吗?
# account credentials
storage_name            = "storage_name"
container_name          = "container_name"
storage_key             = "storage_key"
storage_service_version = "2017-04-17" # fixed version

request_date =
  Timex.now
  |> Timex.format!("{RFC1123}") # Wed, 02 Aug 2017 00:52:10 +0000
  |> String.replace("+0000", "GMT") # Wed, 02 Aug 2017 00:52:10 GMT

# set canonicalized headers
x_ms_date    = "x-ms-date:#{request_date}"
x_ms_version = "x-ms-version:#{storage_service_version}"

# assign values for string_to_sign
verb                   = "GET\n"
content_encoding       = "\n"
content_language       = "\n"
content_length         = "\n"
content_md5            = "\n"
content_type           = "\n"
date                   = "\n"
if_modified_since      = "\n"
if_match               = "\n"
if_none_match          = "\n"
if_unmodified_since    = "\n"
range                  = "\n"
canonicalized_headers  = "#{x_ms_date}\n#{x_ms_version}\n"
canonicalized_resource = "/#{storage_name}/#{container_name}\ncomp:list\nrestype:container" # removed timeout. removed space

# concat string_to_sign
string_to_sign =
  verb                  <>
  content_encoding      <>
  content_language      <>
  content_length        <>
  content_md5           <>
  content_type          <>
  date                  <>
  if_modified_since     <>
  if_match              <>
  if_none_match         <>
  if_unmodified_since   <>
  range                 <>
  canonicalized_headers <>
  canonicalized_resource

# decode storage_key
{:ok, decoded_key} =
  storage_key
  |> Base.decode64

# sign and encode string_to_sign
signature =
  :crypto.hmac(:sha256, decoded_key, string_to_sign)
  |> Base.encode64

# build authorization header
authorization_header = "SharedKey #{storage_name}:#{signature}"

# build request and use HTTPoison
url     = "https://storage_name.blob.core.windows.net/container_name?restype=container&comp=list"
headers = [ # "Date": request_date,
           "x-ms-date": request_date, # fixed typo
           "x-ms-version": storage_service_version, # fixed typo
           # "Accept": "application/json",
           "Authorization": authorization_header]
options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 500]

HTTPoison.get(url, headers, options)

注意事项

我使用/尝试过的一些资源...

【问题讨论】:

    标签: rest azure elixir azure-storage httpoison


    【解决方案1】:

    我注意到的几个问题:

    1. 您在请求中包含了Date 请求标头,但它未包含在您的string_to_sign 中。在您的 string_to_sign 中包含此标头或从请求标头中删除此标头。
    2. 您在canonicalized_resource 中包含timeout:30,但它未包含在您的请求URL 中。同样,在您的请求查询字符串中添加 timeout=30 或从 canonicalized_resource 中删除 timeout:30。
    3. 我没有使用 Elixir,所以我不知道请求标头在那里是如何工作的,但是您将请求标头命名为 x-ms-date-h 和 x-ms-version-h。不应该分别是x-ms-date和x-ms-version吗?

    【讨论】:

    • 感谢您的反馈。 #1 我从标题中删除了Date。 #2 我从caonicalized_resource 中删除了timeout:30。 #3 我删除了-h;那是一个错字。它仍然无法正常工作,但错误消息的Server used following string to sign 部分似乎看起来更好。我已更新问题以反映您的建议并显示最新的错误消息。
    • 我搞定了。我在canonicalized_resource 中有另一个错字。我不得不删除一个“空格”,/#{storage_name}/ #{container_name} -> /#{storage_name}/#{container_name}。我在微软的文档中关注GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Sun, 11 Oct 2009 21:49:13 GMT\nx-ms-version:2009-09-19\n/myaccount/ mycontainer\ncomp:metadata\nrestype:container\ntimeout:20 的示例(有问题的第一个参考);该空间似乎是一个错字...\n/myaccount/ mycontainer\n...。问题中的代码现在应该可以工作了。感谢您的帮助!
    猜你喜欢
    • 2017-10-23
    • 1970-01-01
    • 2019-11-08
    • 2016-12-02
    • 2017-12-21
    • 2016-12-15
    • 2013-05-22
    • 2018-07-19
    • 1970-01-01
    相关资源
    最近更新 更多