使用Clojure调用Azure Blob Rest API遇403:请求Date头错误
问题:Clojure调用Azure Blob REST API报403认证错误
收到的错误信息:
:status 403
:reason-phrase Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
The Date header in the request is incorrect.
用户代码:
(require '[clj-http.client :as http]) (require '[java-time :as jt]) (import javax.crypto.Mac javax.crypto.spec.SecretKeySpec org.apache.commons.codec.binary.Base64) (defn- canonicalized-resource [account-name container-name blob-name] (str "/" account-name "/" container-name "/" blob-name)) (defn get-authorization-header "Returns the Authorization header for a Blob Storage request." [account-name account-key request-date request-method content-type canonicalized-resource version] (let [string-to-sign (str request-method "\n\n\n\n\n" content-type "\n" "\n\n\n\n\n\n" "x-ms-date:" request-date "\n" "x-ms-version:" version "\n" canonicalized-resource) key-bytes (Base64/decodeBase64 account-key) key-spec (SecretKeySpec. key-bytes "HmacSHA256") mac (Mac/getInstance "HmacSHA256") _ (.init mac key-spec) signature-bytes (.doFinal mac (.getBytes string-to-sign)) signature (Base64/encodeBase64String signature-bytes) auth-header (str "SharedKey " account-name ":" signature)] auth-header)) (defn put-blob [account-name account-key container-name blob-name content-type file-path] (let [request-date (jt/format (jt/formatter :rfc-1123-date-time) (jt/zoned-date-time (jt/zone-offset 0))) version "2022-11-02" request-url (str "https://" account-name ".blob.core.windows.net/" container-name "/" blob-name) canon-resource (canonicalized-resource account-name container-name blob-name) auth-header (get-authorization-header account-name account-key request-date "PUT" content-type canon-resource version) file-content (slurp file-path) response (http/put request-url {:body (Base64/encodeBase64 (.getBytes file-content)) :content-type content-type :headers {"Content-Type" content-type "x-ms-date" request-date "x-ms-version" version "x-ms-blob-type" "BlockBlob" "Authorization" auth-header} :debug true})] (if (= (:status response) 201) (:body response) (throw (Exception. (:body response))))))
问题排查与修复方案
1. 日期格式不符合Azure要求
Azure要求x-ms-date必须是带GMT时区标识的RFC 1123格式,原代码生成的日期可能用+00:00替代GMT,触发日期校验失败。
修改日期生成逻辑:
(request-date (jt/format (jt/formatter "EEE, dd MMM yyyy HH:mm:ss 'GMT'" (jt/locale "en")) (jt/instant)))
- 用
jt/instant获取UTC时间,确保时区正确性 - 显式指定格式化字符串,强制输出
GMT标识 - 英文Locale避免日期月份/星期的本地化问题
2. String-to-Sign遗漏必要请求头
Azure Shared Key认证要求所有x-ms-前缀的请求头必须加入String-to-Sign,且按字母顺序排列。原代码遗漏了请求中添加的x-ms-blob-type,导致签名不匹配。
修改get-authorization-header函数:
(defn get-authorization-header "Returns the Authorization header for a Blob Storage request." [account-name account-key request-date request-method content-type canonicalized-resource version blob-type] (let [string-to-sign (str request-method "\n" "\n" ; Content-Encoding "\n" ; Content-Language "\n" ; Content-Length "\n" ; Content-MD5 content-type "\n" "\n" ; Date "\n" ; If-Modified-Since "\n" ; If-Match "\n" ; If-None-Match "\n" ; If-Unmodified-Since "\n" ; Range "x-ms-blob-type:" blob-type "\n" "x-ms-date:" request-date "\n" "x-ms-version:" version "\n" canonicalized-resource) key-bytes (Base64/decodeBase64 account-key) key-spec (SecretKeySpec. key-bytes "HmacSHA256") mac (Mac/getInstance "HmacSHA256") _ (.init mac key-spec) signature-bytes (.doFinal mac (.getBytes string-to-sign "UTF-8")) ; 指定UTF-8编码避免字符集差异 signature (Base64/encodeBase64String signature-bytes) auth-header (str "SharedKey " account-name ":" signature)] auth-header))
- 按字母顺序添加
x-ms-blob-type(b在d前,早于x-ms-date) - 显式指定字符串编码为UTF-8
- 保留每个Header占位符的空行,即使对应Header为空
3. 请求体Base64编码冗余
上传普通文件时,Azure Blob接受原始字节流,无需Base64编码。原代码的编码会导致内容长度不匹配,触发认证错误。
修改请求体处理:
; 文本文件指定编码读取 file-content (slurp file-path :encoding "UTF-8") ; 二进制文件直接传File对象(clj-http自动处理字节流) ; file-content (clojure.java.io/file file-path) (response (http/put request-url {:body file-content :content-type content-type :headers {"Content-Type" content-type "x-ms-date" request-date "x-ms-version" version "x-ms-blob-type" "BlockBlob" "Authorization" auth-header} :debug true}))
4. 更新put-blob的函数调用
修改put-blob中调用get-authorization-header的部分,传入x-ms-blob-type参数:
auth-header (get-authorization-header account-name account-key request-date "PUT" content-type canon-resource version "BlockBlob")
内容的提问来源于stack exchange,提问作者Orlando J. Mendoza
相关产品推荐
相关产品推荐

