簡體   English   中英

如何使用 Elixir 或 Erlang 創建 Google Cloud Storage 簽名 URL?

[英]How to create a Google Cloud Storage Signed URL using Elixir or Erlang?

我目前正在嘗試讓我的 elixir 網絡服務器為 Google Cloud Storage 生成簽名網址,以便我可以生成過期的文件網址。 不幸的是,當我嘗試使用生成的 url 時,出現以下錯誤:

<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>

我能夠生成通過 gsutil 工具工作的簽名 url,雖然它很慢,而且還通過這里給出的 python 示例:

Google Cloud Storage 簽名 URL 示例

我當前在 Elixir 中的實現基於上面的 Python 示例,如下所示:

@default_expiration 1000
  def construct_string(http_verb, content_md5, content_type, expiration_timestamp, canonicalized_extension_headers, canonicalized_resource) do
    "#{http_verb}\n
    #{content_md5}\n
    #{content_type}\n
    #{expiration_timestamp}\n
    #{canonicalized_extension_headers}
    #{canonicalized_resource}"
  end

  def load_secret_pem do
    load_local_key_file("/path/to/key")
  end

  def load_local_key_file(path) do
    {ok, pem_bin} = File.read(path)
    [rsa_entry] = :public_key.pem_decode(pem_bin)
    key = :public_key.pem_entry_decode(rsa_entry)
  end

  def base64Sign(plaintext) do
    key = load_secret_pem()
    signature_bytes = :public_key.sign(plaintext, :sha256, key )
    Base.url_encode64(signature_bytes)
    |> String.replace("-", "%2B")
    |> String.replace("_", "%2F")
    |> URI.encode_www_form
  end

  def make_url(verb, path, content_md5 \\ "", content_type \\ "") do
    client_id = GCloud.Credentials.client_email() |> URI.encode_www_form
    expiration =  :os.system_time(:seconds) + @default_expiration
    base_url = GCloud.Storage.base_uri() <> path
    signature_string = construct_string(verb, content_md5, content_type, expiration, "", path )
    url_encoded_signature = base64Sign(signature_string)
    IO.puts "#{base_url}?GoogleAccessId=#{client_id}&Expires=#{expiration}&Signature=#{url_encoded_signature}"
  end

如何使用 Elixir 或 Erlang 正確簽署已簽名的 url?

您在construct_string字符串構造可能正在做您沒有意識到的事情。 請記住,Python 語法不一樣,並且對空格有其他意見。

defmodule Test do
  def foo(a,b) do
    "#{a}\n
    #{b}"
  end
end
IO.inspect Test.foo(1,2)
# output:
"1\n\n    2"

如果你使用帶有"""的 heredoc,前導空格會消失,但你的換行符仍然重復。不過,這種方法可能是一個壞主意,因為如果你從 Windows 機器保存文件,你可能有\\r\\n作為以編輯器結尾的行,無論如何擺脫這些都是不必要的煩惱。

相反,我認為你應該在這里改變你的方法是這樣的:

def construct_string(http_verb, content_md5, content_type, expiration_timestamp, canonicalized_extension_headers, canonicalized_resource) do
  headers = Enum.join([http_verb, content_md5, content_type, expiration_timestamp], "\n")
  "#{headers}\n#{canonicalized_extension_headers}#{canonicalized_resource}"
end

我不確定是否還有其他錯誤,但這對我來說很明顯。

我設法讓這個工作,我通過並排打開一個 python 和 elixir REPL 來做到這一點,用測試字符串執行每個步驟並比較輸出的差異,散列或簽署測試字符串后沒有差異,但是有base64編碼之后,所以我改變了:

def base64Sign(plaintext) do
    key = load_secret_pem()
    signature_bytes = :public_key.sign(plaintext, :sha256, key )
    Base.url_encode64(signature_bytes)
    |> String.replace("-", "%2B")
    |> String.replace("_", "%2F")
    |> URI.encode_www_form
end

def base64Sign(plaintext) do
    key = GCloud.Credentials.load_secret_pem()
    signature_bytes = :public_key.sign(plaintext, :sha256, key )
    Base.encode64(signature_bytes)
    |> URI.encode_www_form
end

這與asonge 的字符串構造建議相結合解決了這個問題。

使用gcs_signer從hex.pm:

Application.get_env(:goth, :json)
|> Poison.decode!
|> GcsSigner.Client.from_keyfile()
|> GcsSigner.sign_url(bucket, object)

在 Google Cloud Platform 環境(例如 Cloud Functions 和 App Engine)中,您通常不會在實例化期間提供keyFilenamecredentials
那么你可以使用signBlob api看這個例子

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM