如何在swift 3中授权访问AWS API网关?
How to access AWS API gateway by authorization in swift 3?
当我通过 Alamofire 计算 AWS 签名访问 AWS 网关时。
它响应错误消息“我们计算的请求签名与您提供的签名不匹配。检查您的 AWS 秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。 "
有什么方法可以访问 swift 中的 AWS API 网关 3. 如果可能,请您提供一些简单的 swift 代码。提前致谢!
private var configuration:AWSServiceConfiguration?
private var awsCredential = AWSCredential()
private func apiGatewaySimple(){
let date = URLRequestSigner().iso8601()
let xAmzStamp = date.short
guard let URL = URL(string: "xxxxx") else { return }
var request = URLRequest(url: URL)
let url = request.url
let host = url?.host
let credentialProvider = AWSCognitoCredentialsProvider(regionType: .APNortheast1, identityPoolId: Constants.IDENTITY_POOL_ID)
let endpoint = AWSEndpoint.init(region: .APNortheast1, service: .APIGateway, url: url)
self.configuration = AWSServiceConfiguration.init(region: .APNortheast1, endpoint: endpoint, credentialsProvider: credentialProvider)
let signer : AWSSignatureV4Signer = AWSSignatureV4Signer(credentialsProvider: self.configuration?.credentialsProvider, endpoint: endpoint)
self.configuration?.requestInterceptors = [AWSNetworkingRequestInterceptor(),signer]
_ = self.configuration?.responseInterceptors
_ = self.configuration?.endpoint
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let requestDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
_ = dateFormatter.string(from: requestDate)
let params:[String :Any]=["xxx" : "xxx" as AnyObject,
"xxxx" : "xxx" as AnyObject ]
let jsonData = try? JSONSerialization.data(withJSONObject: params, options: [])
let json = jsonData
request.httpMethod = HttpMethod.post.rawValue
request.httpBody = jsonData as! Data
request.setValue(date.full, forHTTPHeaderField: "X-Amz-Date")
request.setValue(self.awsCredential.sessionKey, forHTTPHeaderField: "X-Amz-Security-Token")
request.setValue(json?.count.description, forHTTPHeaderField: "Content-Length")
request.setValue(host, forHTTPHeaderField: "Host")
let contentLength = json?.count
let cfpath = request.url
let query = cfpath?.query
let hash = AWSSignatureSignerUtility.hash(request.httpBody)
let contentsha256 = AWSSignatureSignerUtility.hexEncode(NSString.init(data: hash!, encoding: String.Encoding.ascii.rawValue)! as String)
let canonicalRequest = AWSSignatureV4Signer.getCanonicalizedRequest(request.httpMethod, path: "xxxxx", query: query, headers: request.allHTTPHeaderFields, contentSha256: contentsha256)
let scope = String(format: "%@/%@/%@/%@", xAmzStamp, "ap-northeast-1","execute-api",AWSSignatureV4Terminator)
let signingCredential = String(format: "%@/%@", self.awsCredential.accessKey!,scope)
let awsSignatureSignerV4 = AWSSignatureV4Signer(credentialsProvider: configuration?.credentialsProvider,endpoint:endpoint)
_ = awsSignatureSignerV4?.interceptRequest(request as! NSMutableURLRequest)
let canonicalRequestHash = AWSSignatureSignerUtility.hashString(canonicalRequest)
let stringToSign = String(format: "%@/%@/%@/%@",AWSSignatureV4Algorithm,request.value(forHTTPHeaderField: "X-Amz-Date")!,scope,AWSSignatureSignerUtility.hexEncode(canonicalRequestHash))
let kSigning = AWSSignatureV4Signer.getV4DerivedKey(self.awsCredential.secretKey, date: xAmzStamp, region: "ap-northeast-1", service: "execute-api")
let signature = AWSSignatureSignerUtility.sha256HMac(with: stringToSign.data(using: .utf8), withKey: kSigning)
let credentialsAuthorizationHeader = String(format: "Credential=%@", signingCredential)
let signedHeadersAuthorizationHeader = String(format: "SignedHeaders=%@", AWSSignatureV4Signer.getSignedHeadersString(request.allHTTPHeaderFields))
let signatureAuthorizationHeader = String(format: "Signature=%@", AWSSignatureSignerUtility.hexEncode(NSString.init(data: signature!, encoding: String.Encoding.ascii.rawValue)! as String))
let authorization = String(format: "%@ %@, %@, %@", AWSSignatureV4Algorithm,credentialsAuthorizationHeader,signedHeadersAuthorizationHeader,signatureAuthorizationHeader)
let headers = [
"Content-Type": "application/json" ,
"x-amz-security-token" : self.awsCredential.sessionKey ?? "",
"x-amz-date" : date.full,
"Content-Length" : contentLength?.description ?? "0",
"Authorization" : authorization,
"Host" : host ?? ""
]
Alamofire.request(request).responseString{ (response: DataResponse<String>) in
print("\(response.result.value)")
}
}
下面的代码对我来说效果很好。
func getUrlRequest(url:String,params:[String :Any]) -> URLRequest{
var headers = [String :String]()
var signedHeaders = [String:String]()
var bodyDigest = sha256("")
var urlForSigning = url
if urlForSigning.last == "/" {
urlForSigning = String(url.dropLast())
}
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = HttpMethod.post.rawValue
let body = try? JSONSerialization.data(withJSONObject: params, options: [])
let bodyString = NSString(data: body!, encoding: String.Encoding.utf8.rawValue)! as String
if (bodyString.trim().count > 0){
bodyDigest = sha256(bodyString)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = bodyString.data(using: String.Encoding.utf8)
}
signedHeaders = self.signedHeaders(url: URL(string: urlForSigning)!,
bodyDigest: bodyDigest, httpMethod: HttpMethod.post.rawValue)
headers["Authorization"] = signedHeaders["Authorization"]
headers["x-amz-date"] = signedHeaders["x-amz-date"]
headers["Host"] = signedHeaders["Host"]
headers["expiration"] = signedHeaders["expiration"]
headers["x-amz-security-token"] = signedHeaders["x-amz-security-token"]
headers["Content-Type"] = signedHeaders["Content-Type"]
for (k, v) in headers {
request.setValue(v, forHTTPHeaderField: k)
}
return request
}
func signedHeaders(url: URL, bodyDigest: String,
httpMethod: String, date: Date = Date()) -> [String: String] {
let datetime = timestamp(date)
let expirationTime = timestamp(self.expiration)
let port = ((url as NSURL).port != nil) ? ":" + String(describing: (url as NSURL).port!) : ""
var headers = ["x-amz-date": datetime, "Host": url.host! + port, "expiration": expirationTime, "x-amz-security-token" : self.sessionKey, "Content-Type": "application/json"]
headers["Authorization"] = authorization(accessKey, secretKey: secretKey, url: url, headers: headers,
datetime: datetime, httpMethod: httpMethod, bodyDigest: bodyDigest)
return headers
}
// MARK: Utilities
fileprivate func pathForURL(_ url: URL) -> String {
var path = url.path
if path.isEmpty {
path = "/"
} else {
// do this to preserve encoded path fragments, like those containing encoded '/' (%2F)
// NSURL.path decodes them and they are lost
var encodedPartsArray = [String]()
// get rid of 'http(s)://'
let fullURL = String(url.absoluteString[url.absoluteString.index(url.absoluteString.startIndex, offsetBy: 8)...])
var rawPath = String(fullURL[fullURL.range(of: "/")!.lowerBound...])
if rawPath.contains("?") {
rawPath = String(rawPath[..<rawPath.range(of: "?")!.lowerBound])
}
for part in rawPath.components(separatedBy: "/") {
if !part.isEmpty {
encodedPartsArray.append(Signer.encodeURIComponent(part))
}
}
path = "/" + encodedPartsArray.joined(separator: "/")
}
return path
}
func sha256(_ str: String) -> String {
let data = str.data(using: String.Encoding.utf8)!
return data.sha256().toHexString()
}
fileprivate func hmac(_ string: NSString, key: Data) -> Data {
let msg = string.data(using: String.Encoding.utf8.rawValue)!.bytes
let hmac:[UInt8] = try! HMAC(key: key.bytes, variant: .sha256).authenticate(msg)
return Data(bytes: hmac)
}
fileprivate func timestamp(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter.string(from: date)
}
// MARK: Methods Ported from AWS SDK
fileprivate func authorization(_ accessKey: String, secretKey: String, url: URL, headers: Dictionary<String, String>,
datetime: String, httpMethod: String, bodyDigest: String) -> String {
let cred = credential(accessKey, datetime: datetime)
let shead = signedHeaders(headers)
let sig = signature(secretKey, url: url, headers: headers, datetime: datetime,
httpMethod: httpMethod, bodyDigest: bodyDigest)
return [
"AWS4-HMAC-SHA256 Credential=\(cred)",
"SignedHeaders=\(shead)",
"Signature=\(sig)",
].joined(separator: ", ")
}
fileprivate func credential(_ accessKey: String, datetime: String) -> String {
return "\(accessKey)/\(credentialScope(datetime))"
}
fileprivate func signedHeaders(_ headers: [String:String]) -> String {
var list = Array(headers.keys).map { [=10=].lowercased() }.sorted()
if let itemIndex = list.index(of: "authorization") {
list.remove(at: itemIndex)
}
return list.joined(separator: ";")
}
fileprivate func canonicalHeaders(_ headers: [String: String]) -> String {
var list = [String]()
let keys = Array(headers.keys).sorted {[=10=].localizedCompare() == ComparisonResult.orderedAscending}
for key in keys {
if key.caseInsensitiveCompare("authorization") != ComparisonResult.orderedSame {
// Note: This does not strip whitespace, but the spec says it should
list.append("\(key.lowercased()):\(headers[key]!)")
}
}
return list.joined(separator: "\n")
}
fileprivate func signature(_ secretKey: String, url: URL, headers: [String: String],
datetime: String, httpMethod: String, bodyDigest: String) -> String {
let secret = NSString(format: "AWS4%@", secretKey).data(using: String.Encoding.utf8.rawValue)!
let date = hmac(String(datetime[..<datetime.index(datetime.startIndex, offsetBy: 8)]) as NSString, key: secret)
let region = hmac(regionName as NSString, key: date)
let service = hmac(serviceName as NSString, key: region)
let credentials = hmac("aws4_request", key: service)
let string = stringToSign(datetime, url: url, headers: headers, httpMethod: httpMethod, bodyDigest: bodyDigest)
return hmac(string as NSString, key: credentials).toHexString()
}
fileprivate func credentialScope(_ datetime: String) -> String {
return [
String(datetime[..<datetime.index(datetime.startIndex, offsetBy: 8)]),
regionName,
serviceName,
"aws4_request"
].joined(separator: "/")
}
fileprivate func stringToSign(_ datetime: String, url: URL, headers: [String: String],
httpMethod: String, bodyDigest: String) -> String {
return [
"AWS4-HMAC-SHA256",
datetime,
credentialScope(datetime),
sha256(canonicalRequest(url, headers: headers, httpMethod: httpMethod, bodyDigest: bodyDigest)),
].joined(separator: "\n")
}
fileprivate func canonicalRequest(_ url: URL, headers: [String: String],
httpMethod: String, bodyDigest: String) -> String {
return [
httpMethod, // HTTP Method
pathForURL(url), // Resource Path
url.query ?? "", // Canonicalized Query String
"\(canonicalHeaders(headers))\n", // Canonicalized Header String (Plus a newline for some reason)
signedHeaders(headers), // Signed Headers String
bodyDigest, // Sha265 of Body
].joined(separator: "\n")
}
open static func encodeURIComponent(_ s: String) -> String {
let allowed = NSMutableCharacterSet.alphanumeric()
allowed.addCharacters(in: "-_.~")
//allowed.addCharactersInString("-_.!~*'()")
return s.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet) ?? ""
}
当我通过 Alamofire 计算 AWS 签名访问 AWS 网关时。 它响应错误消息“我们计算的请求签名与您提供的签名不匹配。检查您的 AWS 秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。 "
有什么方法可以访问 swift 中的 AWS API 网关 3. 如果可能,请您提供一些简单的 swift 代码。提前致谢!
private var configuration:AWSServiceConfiguration?
private var awsCredential = AWSCredential()
private func apiGatewaySimple(){
let date = URLRequestSigner().iso8601()
let xAmzStamp = date.short
guard let URL = URL(string: "xxxxx") else { return }
var request = URLRequest(url: URL)
let url = request.url
let host = url?.host
let credentialProvider = AWSCognitoCredentialsProvider(regionType: .APNortheast1, identityPoolId: Constants.IDENTITY_POOL_ID)
let endpoint = AWSEndpoint.init(region: .APNortheast1, service: .APIGateway, url: url)
self.configuration = AWSServiceConfiguration.init(region: .APNortheast1, endpoint: endpoint, credentialsProvider: credentialProvider)
let signer : AWSSignatureV4Signer = AWSSignatureV4Signer(credentialsProvider: self.configuration?.credentialsProvider, endpoint: endpoint)
self.configuration?.requestInterceptors = [AWSNetworkingRequestInterceptor(),signer]
_ = self.configuration?.responseInterceptors
_ = self.configuration?.endpoint
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let requestDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
_ = dateFormatter.string(from: requestDate)
let params:[String :Any]=["xxx" : "xxx" as AnyObject,
"xxxx" : "xxx" as AnyObject ]
let jsonData = try? JSONSerialization.data(withJSONObject: params, options: [])
let json = jsonData
request.httpMethod = HttpMethod.post.rawValue
request.httpBody = jsonData as! Data
request.setValue(date.full, forHTTPHeaderField: "X-Amz-Date")
request.setValue(self.awsCredential.sessionKey, forHTTPHeaderField: "X-Amz-Security-Token")
request.setValue(json?.count.description, forHTTPHeaderField: "Content-Length")
request.setValue(host, forHTTPHeaderField: "Host")
let contentLength = json?.count
let cfpath = request.url
let query = cfpath?.query
let hash = AWSSignatureSignerUtility.hash(request.httpBody)
let contentsha256 = AWSSignatureSignerUtility.hexEncode(NSString.init(data: hash!, encoding: String.Encoding.ascii.rawValue)! as String)
let canonicalRequest = AWSSignatureV4Signer.getCanonicalizedRequest(request.httpMethod, path: "xxxxx", query: query, headers: request.allHTTPHeaderFields, contentSha256: contentsha256)
let scope = String(format: "%@/%@/%@/%@", xAmzStamp, "ap-northeast-1","execute-api",AWSSignatureV4Terminator)
let signingCredential = String(format: "%@/%@", self.awsCredential.accessKey!,scope)
let awsSignatureSignerV4 = AWSSignatureV4Signer(credentialsProvider: configuration?.credentialsProvider,endpoint:endpoint)
_ = awsSignatureSignerV4?.interceptRequest(request as! NSMutableURLRequest)
let canonicalRequestHash = AWSSignatureSignerUtility.hashString(canonicalRequest)
let stringToSign = String(format: "%@/%@/%@/%@",AWSSignatureV4Algorithm,request.value(forHTTPHeaderField: "X-Amz-Date")!,scope,AWSSignatureSignerUtility.hexEncode(canonicalRequestHash))
let kSigning = AWSSignatureV4Signer.getV4DerivedKey(self.awsCredential.secretKey, date: xAmzStamp, region: "ap-northeast-1", service: "execute-api")
let signature = AWSSignatureSignerUtility.sha256HMac(with: stringToSign.data(using: .utf8), withKey: kSigning)
let credentialsAuthorizationHeader = String(format: "Credential=%@", signingCredential)
let signedHeadersAuthorizationHeader = String(format: "SignedHeaders=%@", AWSSignatureV4Signer.getSignedHeadersString(request.allHTTPHeaderFields))
let signatureAuthorizationHeader = String(format: "Signature=%@", AWSSignatureSignerUtility.hexEncode(NSString.init(data: signature!, encoding: String.Encoding.ascii.rawValue)! as String))
let authorization = String(format: "%@ %@, %@, %@", AWSSignatureV4Algorithm,credentialsAuthorizationHeader,signedHeadersAuthorizationHeader,signatureAuthorizationHeader)
let headers = [
"Content-Type": "application/json" ,
"x-amz-security-token" : self.awsCredential.sessionKey ?? "",
"x-amz-date" : date.full,
"Content-Length" : contentLength?.description ?? "0",
"Authorization" : authorization,
"Host" : host ?? ""
]
Alamofire.request(request).responseString{ (response: DataResponse<String>) in
print("\(response.result.value)")
}
}
下面的代码对我来说效果很好。
func getUrlRequest(url:String,params:[String :Any]) -> URLRequest{
var headers = [String :String]()
var signedHeaders = [String:String]()
var bodyDigest = sha256("")
var urlForSigning = url
if urlForSigning.last == "/" {
urlForSigning = String(url.dropLast())
}
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = HttpMethod.post.rawValue
let body = try? JSONSerialization.data(withJSONObject: params, options: [])
let bodyString = NSString(data: body!, encoding: String.Encoding.utf8.rawValue)! as String
if (bodyString.trim().count > 0){
bodyDigest = sha256(bodyString)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = bodyString.data(using: String.Encoding.utf8)
}
signedHeaders = self.signedHeaders(url: URL(string: urlForSigning)!,
bodyDigest: bodyDigest, httpMethod: HttpMethod.post.rawValue)
headers["Authorization"] = signedHeaders["Authorization"]
headers["x-amz-date"] = signedHeaders["x-amz-date"]
headers["Host"] = signedHeaders["Host"]
headers["expiration"] = signedHeaders["expiration"]
headers["x-amz-security-token"] = signedHeaders["x-amz-security-token"]
headers["Content-Type"] = signedHeaders["Content-Type"]
for (k, v) in headers {
request.setValue(v, forHTTPHeaderField: k)
}
return request
}
func signedHeaders(url: URL, bodyDigest: String,
httpMethod: String, date: Date = Date()) -> [String: String] {
let datetime = timestamp(date)
let expirationTime = timestamp(self.expiration)
let port = ((url as NSURL).port != nil) ? ":" + String(describing: (url as NSURL).port!) : ""
var headers = ["x-amz-date": datetime, "Host": url.host! + port, "expiration": expirationTime, "x-amz-security-token" : self.sessionKey, "Content-Type": "application/json"]
headers["Authorization"] = authorization(accessKey, secretKey: secretKey, url: url, headers: headers,
datetime: datetime, httpMethod: httpMethod, bodyDigest: bodyDigest)
return headers
}
// MARK: Utilities
fileprivate func pathForURL(_ url: URL) -> String {
var path = url.path
if path.isEmpty {
path = "/"
} else {
// do this to preserve encoded path fragments, like those containing encoded '/' (%2F)
// NSURL.path decodes them and they are lost
var encodedPartsArray = [String]()
// get rid of 'http(s)://'
let fullURL = String(url.absoluteString[url.absoluteString.index(url.absoluteString.startIndex, offsetBy: 8)...])
var rawPath = String(fullURL[fullURL.range(of: "/")!.lowerBound...])
if rawPath.contains("?") {
rawPath = String(rawPath[..<rawPath.range(of: "?")!.lowerBound])
}
for part in rawPath.components(separatedBy: "/") {
if !part.isEmpty {
encodedPartsArray.append(Signer.encodeURIComponent(part))
}
}
path = "/" + encodedPartsArray.joined(separator: "/")
}
return path
}
func sha256(_ str: String) -> String {
let data = str.data(using: String.Encoding.utf8)!
return data.sha256().toHexString()
}
fileprivate func hmac(_ string: NSString, key: Data) -> Data {
let msg = string.data(using: String.Encoding.utf8.rawValue)!.bytes
let hmac:[UInt8] = try! HMAC(key: key.bytes, variant: .sha256).authenticate(msg)
return Data(bytes: hmac)
}
fileprivate func timestamp(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter.string(from: date)
}
// MARK: Methods Ported from AWS SDK
fileprivate func authorization(_ accessKey: String, secretKey: String, url: URL, headers: Dictionary<String, String>,
datetime: String, httpMethod: String, bodyDigest: String) -> String {
let cred = credential(accessKey, datetime: datetime)
let shead = signedHeaders(headers)
let sig = signature(secretKey, url: url, headers: headers, datetime: datetime,
httpMethod: httpMethod, bodyDigest: bodyDigest)
return [
"AWS4-HMAC-SHA256 Credential=\(cred)",
"SignedHeaders=\(shead)",
"Signature=\(sig)",
].joined(separator: ", ")
}
fileprivate func credential(_ accessKey: String, datetime: String) -> String {
return "\(accessKey)/\(credentialScope(datetime))"
}
fileprivate func signedHeaders(_ headers: [String:String]) -> String {
var list = Array(headers.keys).map { [=10=].lowercased() }.sorted()
if let itemIndex = list.index(of: "authorization") {
list.remove(at: itemIndex)
}
return list.joined(separator: ";")
}
fileprivate func canonicalHeaders(_ headers: [String: String]) -> String {
var list = [String]()
let keys = Array(headers.keys).sorted {[=10=].localizedCompare() == ComparisonResult.orderedAscending}
for key in keys {
if key.caseInsensitiveCompare("authorization") != ComparisonResult.orderedSame {
// Note: This does not strip whitespace, but the spec says it should
list.append("\(key.lowercased()):\(headers[key]!)")
}
}
return list.joined(separator: "\n")
}
fileprivate func signature(_ secretKey: String, url: URL, headers: [String: String],
datetime: String, httpMethod: String, bodyDigest: String) -> String {
let secret = NSString(format: "AWS4%@", secretKey).data(using: String.Encoding.utf8.rawValue)!
let date = hmac(String(datetime[..<datetime.index(datetime.startIndex, offsetBy: 8)]) as NSString, key: secret)
let region = hmac(regionName as NSString, key: date)
let service = hmac(serviceName as NSString, key: region)
let credentials = hmac("aws4_request", key: service)
let string = stringToSign(datetime, url: url, headers: headers, httpMethod: httpMethod, bodyDigest: bodyDigest)
return hmac(string as NSString, key: credentials).toHexString()
}
fileprivate func credentialScope(_ datetime: String) -> String {
return [
String(datetime[..<datetime.index(datetime.startIndex, offsetBy: 8)]),
regionName,
serviceName,
"aws4_request"
].joined(separator: "/")
}
fileprivate func stringToSign(_ datetime: String, url: URL, headers: [String: String],
httpMethod: String, bodyDigest: String) -> String {
return [
"AWS4-HMAC-SHA256",
datetime,
credentialScope(datetime),
sha256(canonicalRequest(url, headers: headers, httpMethod: httpMethod, bodyDigest: bodyDigest)),
].joined(separator: "\n")
}
fileprivate func canonicalRequest(_ url: URL, headers: [String: String],
httpMethod: String, bodyDigest: String) -> String {
return [
httpMethod, // HTTP Method
pathForURL(url), // Resource Path
url.query ?? "", // Canonicalized Query String
"\(canonicalHeaders(headers))\n", // Canonicalized Header String (Plus a newline for some reason)
signedHeaders(headers), // Signed Headers String
bodyDigest, // Sha265 of Body
].joined(separator: "\n")
}
open static func encodeURIComponent(_ s: String) -> String {
let allowed = NSMutableCharacterSet.alphanumeric()
allowed.addCharacters(in: "-_.~")
//allowed.addCharactersInString("-_.!~*'()")
return s.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet) ?? ""
}