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

Swift通过Alamofire调用AWS加密API时签名及认证头生成问题

基于Alamofire访问AWS受保护API的签名实现方案

核心实现逻辑

AWS受保护的API默认使用SigV4签名算法校验请求合法性,你需要按照SigV4规则,结合你的access key、secret key、请求所属区域、服务名生成签名,再将签名相关信息附加到请求头中,流程和Android端AwsInterceptor逻辑一致。
你可以通过Alamofire提供的RequestInterceptor协议实现全局请求签名拦截,无需手动在每个请求中处理签名逻辑。

完整实现代码

1. 实现AWS SigV4签名拦截器

import Alamofire
import CryptoKit // 系统内置加密库,无需额外引入

// 存储AWS签名所需配置的结构体
struct AWSConfig {
    let accessKey: String
    let secretKey: String
    let region: String
    let service: String
    // 临时凭证需要补充sessionToken字段,按需添加即可
}

class AWSRequestInterceptor: RequestInterceptor {
    private let config: AWSConfig
    
    init(config: AWSConfig) {
        self.config = config
    }
    
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        var signedRequest = urlRequest
        
        // 1. 补充请求必要的基础头
        let dateFormatter = DateFormatter()
        dateFormatter.timeZone = TimeZone(identifier: "UTC")
        dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
        let amzDate = dateFormatter.string(from: Date())
        signedRequest.setValue(amzDate, forHTTPHeaderField: "X-Amz-Date")
        
        dateFormatter.dateFormat = "yyyyMMdd"
        let dateStamp = dateFormatter.string(from: Date())
        
        // 2. 构造待签名的内容,按SigV4规则拼接
        guard let url = signedRequest.url,
              let host = url.host,
              let method = signedRequest.httpMethod else {
            completion(.success(urlRequest))
            return
        }
        signedRequest.setValue(host, forHTTPHeaderField: "Host")
        
        let canonicalURI = url.path.isEmpty ? "/" : url.path
        let canonicalQuery = url.query ?? ""
        
        // 构造规范头
        let canonicalHeaders = "host:\(host)\nx-amz-date:\(amzDate)\n"
        let signedHeaders = "host;x-amz-date"
        
        // 计算请求体哈希
        let bodyHash = (signedRequest.httpBody ?? Data()).sha256HexString()
        
        // 拼接规范请求
        let canonicalRequest = """
        \(method)
        \(canonicalURI)
        \(canonicalQuery)
        \(canonicalHeaders)
        \(signedHeaders)
        \(bodyHash)
        """
        
        // 3. 构造待签名字符串
        let algorithm = "AWS4-HMAC-SHA256"
        let credentialScope = "\(dateStamp)/\(config.region)/\(config.service)/aws4_request"
        let stringToSign = """
        \(algorithm)
        \(amzDate)
        \(credentialScope)
        \(canonicalRequest.sha256HexString())
        """
        
        // 4. 计算签名
        guard let signature = calculateSignature(
            secretKey: config.secretKey,
            dateStamp: dateStamp,
            region: config.region,
            service: config.service,
            stringToSign: stringToSign
        ) else {
            completion(.success(urlRequest))
            return
        }
        
        // 5. 构造授权头并附加到请求
        let authorizationHeader = "\(algorithm) Credential=\(config.accessKey)/\(credentialScope), SignedHeaders=\(signedHeaders), Signature=\(signature)"
        signedRequest.setValue(authorizationHeader, forHTTPHeaderField: "Authorization")
        
        completion(.success(signedRequest))
    }
    
    // SigV4签名计算逻辑
    private func calculateSignature(secretKey: String, dateStamp: String, region: String, service: String, stringToSign: String) -> String? {
        let kSecret = "AWS4\(secretKey)".data(using: .utf8)!
        let kDate = HMAC<SHA256>.authenticationCode(for: dateStamp.data(using: .utf8)!, using: SymmetricKey(data: kSecret))
        let kRegion = HMAC<SHA256>.authenticationCode(for: region.data(using: .utf8)!, using: SymmetricKey(data: Data(kDate)))
        let kService = HMAC<SHA256>.authenticationCode(for: service.data(using: .utf8)!, using: SymmetricKey(data: Data(kRegion)))
        let kSigning = HMAC<SHA256>.authenticationCode(for: "aws4_request".data(using: .utf8)!, using: SymmetricKey(data: Data(kService)))
        let signature = HMAC<SHA256>.authenticationCode(for: stringToSign.data(using: .utf8)!, using: SymmetricKey(data: Data(kSigning)))
        return Data(signature).map { String(format: "%02hhx", $0) }.joined()
    }
}

// 扩展Data加SHA256转十六进制方法
extension Data {
    func sha256HexString() -> String {
        let hash = SHA256.hash(data: self)
        return hash.map { String(format: "%02hhx", $0) }.joined()
    }
}

extension String {
    func sha256HexString() -> String {
        return self.data(using: .utf8)!.sha256HexString()
    }
}

2. 修改原有请求代码

在发起请求时传入上面实现的拦截器即可:

// 先初始化AWS配置,替换为你的实际参数
let awsConfig = AWSConfig(
    accessKey: "你的AccessKey",
    secretKey: "你的SecretKey",
    region: "你的服务区域,比如us-east-1",
    service: "你的服务名,比如execute-api对应AWS API Gateway"
)
let awsInterceptor = AWSRequestInterceptor(config: awsConfig)

func secure(secureAPI: securedApi, completionHandler: @escaping (Bool, String) -> ()){
    let headers: HTTPHeaders = [
        .contentType("application/json")
    ]
    // 发起请求时传入interceptor参数
    AF.request(
        secure as! URLConvertible,
        method: .post,
        parameters: secureAPI,
        encoder: JSONParameterEncoder.default,
        headers: headers,
        interceptor: awsInterceptor // 加入签名拦截器
    ).responseJSON{
        response in 
        debugPrint(response)
        switch response.result{
        case.success(let data):
            if response.response?.statusCode == 200{
                print("Success")
                // 按需补充成功逻辑
            }else{
                completionHandler(false, "Please try again")
            }
        case.failure(let err):
            print(err.localizedDescription)
            completionHandler(false, "Please try again")
        }
    }
}

注意事项

  • 如果你的请求需要携带其他自定义头,需要将对应的头名加入到signedHeaders列表中,同时按字母顺序拼接到canonicalHeaders里
  • 若使用临时凭证,需要额外在请求头加入X-Amz-Security-Token字段,同时将该头加入签名头列表
  • 请确保你的设备时间和标准UTC时间误差不超过15分钟,否则会出现签名过期错误

内容的提问来源于stack exchange,提问作者P176061 Haider Tamsil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 23:06:00