![](/img/trans.png)
[英]How do you create and load modules dynamically at runtime in Elixir, or Erlang?
[英]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)中,您通常不會在實例化期間提供keyFilename
或credentials
,
那么你可以使用signBlob api看這個例子
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.