You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 5+CryptoKit调用Azure Table Service Insert Entity遇403认证错误

Azure Table Service 403认证失败问题修复(Swift 5 + CryptoKit)

核心问题分析

你的403错误根源在于签名构造字符串格式错误,同时请求头的部分配置不符合Azure Storage的要求:

  • 签名字符串中错误地将accessKey插入到了Content-MD5的位置,Azure要求该位置为空(如果没有Content-MD5的话)
  • 请求日期头推荐使用x-ms-date而非Date,且签名中的日期必须与请求头完全一致
  • Content-Length计算逻辑错误,未使用实际请求体的长度

修正后的完整代码

class DataService: ObservableObject{
    let requestURI: String = "https://MYACCOUNT.table.core.windows.net/TABLENAME"
    let xmsVersion: String = "2021-12-02"
    let dataServiceVersion: String = "3.0;NetFx"
    
    let accessKey: String = "your-access-key-here"
    var verb: String = "POST"
    let contentType: String = "application/json"
    let canonicalizedResource: String = "/MYACCOUNT/TABLENAME"
    
    //GENERATE REQUEST
    private func generateRequest() -> URLRequest{
        guard let url = URL(string: requestURI) else {
            fatalError("Invalid URL")
        }
        var request = URLRequest(url: url)
        
        let date = getDate()
        let requestBody = getJSON()
        let contentLength = requestBody?.count ?? 0
        
        //AUTHORIZATION HEADER
        let authorizationHeader = generateAuthorizationHeader(date: date)
        print("Authorization: ", authorizationHeader)
        
        request.httpMethod = verb
        request.addValue(authorizationHeader, forHTTPHeaderField: "Authorization")
        request.addValue(date, forHTTPHeaderField: "x-ms-date") // 使用x-ms-date而非Date
        request.addValue(contentType, forHTTPHeaderField: "Content-Type")
        request.addValue(xmsVersion, forHTTPHeaderField: "x-ms-version")
        request.addValue(String(contentLength), forHTTPHeaderField: "Content-Length")
        request.addValue(dataServiceVersion, forHTTPHeaderField: "DataServiceVersion")
        
        request.httpBody = requestBody
        
        return request
    }
    
    //SEND REQUEST
    func sendRequest(){
        let session = URLSession.shared
        let request = generateRequest()
        
        let task = session.dataTask(with: request) { data, response, error in
            print("\nRESPONSE:\n\(response ?? "No response")")
            if let data = data {
                print("\nRESPONSE DATA:\n\(String(data: data, encoding: .utf8) ?? "Invalid data")")
            }
            print("\nERROR:\n\(error ?? "No error")")
        }
        task.resume()
    }
    
    private func generateKey() -> SymmetricKey {
        guard let decodedKey = Data(base64Encoded: accessKey) else {
            fatalError("Invalid access key")
        }
        return SymmetricKey(data: decodedKey)
    }
    
    private func generateAuthorizationHeader(date: String) -> String{
        let key = generateKey()
        
        // 正确的签名字符串格式:VERB + 换行 + Content-MD5(空) + 换行 + Content-Type + 换行 + Date + 换行 + CanonicalizedResource
        let signatureString = """
        \(verb)
        
        \(contentType)
        \(date)
        \(canonicalizedResource)
        """

        guard let signingData = signatureString.data(using: .utf8) else {
            fatalError("Failed to convert signature string to data")
        }
        
        let signature = HMAC<SHA256>.authenticationCode(for: signingData, using: key)
        // 简化CryptoKit签名转Data的方式
        let signatureData = Data(signature)
        let encodedSignature = signatureData.base64EncodedString()

        return "SharedKey MYACCOUNT:\(encodedSignature)"
    }

    private func getDate() -> String{
        let date = Date()
        let dateFormatter = DateFormatter()
        dateFormatter.timeZone = TimeZone(identifier: "GMT")
        dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        let httpDate = dateFormatter.string(from: date)
        print("Date:", httpDate)
        return httpDate
    }
    
    // 示例:返回符合Table Service要求的JSON实体
    private func getJSON() -> Data? {
        let entity = [
            "PartitionKey": "test-partition",
            "RowKey": "test-row-1",
            "Name": "Test Entity"
        ]
        return try? JSONSerialization.data(withJSONObject: entity)
    }
}

关键修正点说明

  • 签名字符串格式修正:移除了错误的accessKey字段,保留Content-MD5的空行位置(因为我们没有使用Content-MD5)
  • 日期头调整:改用x-ms-date头,这是Azure Storage推荐的日期传递方式,避免时区等问题
  • Content-Length计算:直接使用实际请求体的长度,确保与发送的内容一致
  • CryptoKit签名简化:利用Data(signature)直接将HMAC结果转为Data,无需手动操作内存指针
  • 添加必要的请求头:恢复DataServiceVersion头,这是Table Service接口要求的

内容的提问来源于stack exchange,提问作者dakjac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:35:01