根据华为API网关应用程序认证文档,我正在尝试通过应用程序认证访问API,标准化请求内容,然后签署请求。作为客户端,我必须遵循与API网关相同的请求规范,这样每个HTTP请求都可以从前端和后端获得相同的签名结果,以完成身份验证。
我用下面的课来完成它。然而,文档确实很难理解。返回错误Incorrect app authentication information: Authorization format is incorrect
APIG.0303
。
我无法完成这个过程,如果你能提供任何样本来完成这项工作,我真的很感激。
import Foundation
import CommonCrypto
public class SimpleHMACAuth {
/// APP key to authenticate with
public var appKey: String?
/// Secret key to authenticate with
public var secret: String?
/// Algorithm to generate HMAC hash with
public var algorithm: HMACAlgorithm = .sha256
public enum HMACAlgorithm: String {
case sha256 = "sha256"
case sha512 = "sha512"
}
enum RequestSigningError: Error {
case missingAPPKey
case missingSecret
case invalidAlgorithm
case invalidURL
}
fileprivate let dateFormatter: DateFormatter
/// Instantiate with an APP key and secret
/// - Parameters:
/// - appKey: APP key
/// - secret: Secret key
public convenience init(appKey: String, secret: String) {
self.init()
self.appKey = appKey
self.secret = secret
}
/// Instantiate
public init() {
self.dateFormatter = DateFormatter()
self.dateFormatter.dateFormat = "dd MMM yyyy HH:mm:ss zzz"
}
/// Returns a signed version of an input request
/// - Parameter request: Request to sign
/// - Throws: If the request, APP key, secret, or algorithm are invalid
/// - Returns: Signed version of the request
public func sign(_ request: URLRequest) throws -> URLRequest {
guard let signedRequest = (request as NSURLRequest).mutableCopy() as? NSMutableURLRequest else {
throw RequestSigningError.invalidURL
}
guard let appKey = self.appKey else {
throw RequestSigningError.missingAPPKey
}
guard let secret = self.secret else {
throw RequestSigningError.missingSecret
}
// Add the "Authorization" header
signedRequest.addValue("APP key (appKey)", forHTTPHeaderField: "Authorization")
// Confirm the "Date" header exists, and add it if not
if signedRequest.value(forHTTPHeaderField: "Date") == nil {
signedRequest.addValue(self.dateFormatter.string(from: Date()), forHTTPHeaderField: "X-Sdk-Date")
}
// If this request has a body but does not yet have the "Content-Length" header, calculate and append it
if let data = signedRequest.httpBody, signedRequest.value(forHTTPHeaderField: "Content-Length") == nil {
signedRequest.addValue("(data.count)", forHTTPHeaderField: "Content-Length")
}
// Canonicalize the request
let canonicalized = try self.canonicalize(signedRequest as URLRequest)
// Generate a signature from the canonicalized representation of the request
let signature = try self.signature(canonicalized: canonicalized, secret: secret, algorithm: algorithm);
// Append the "Signature" header
signedRequest.addValue("SDK-HMAC-SHA256", forHTTPHeaderField: "Authorization")
signedRequest.addValue(appKey, forHTTPHeaderField: "Access")
signedRequest.addValue("SDK-HMAC-SHA256 (signature)", forHTTPHeaderField: "Signature")
return signedRequest as URLRequest
}
/// Generate a string for a request
/// - Parameter request: Request to sign
/// - Throws: Throws if request is invalid
/// - Returns: Signed request
internal func canonicalize(_ request: URLRequest) throws -> String {
guard let url = request.url else {
throw RequestSigningError.invalidURL
}
let method = (request.httpMethod ?? "GET").uppercased()
var path = url.path
let queryString = url.query != nil ? "?(url.query!)" : ""
let allHeaders = request.allHTTPHeaderFields ?? [String : String]()
let data = request.httpBody ?? Data()
if let component = URLComponents(string: url.absoluteString) {
path = component.path
}
// Only sign these headers
let allowedHeaders = [
"authorization",
"date",
"content-length",
"content-type"
]
// Create a new list of headers, with the keys all lower case
var headers = [String: String]();
for (key, value) in allHeaders {
let lowerCaseKey = key.lowercased()
if allowedHeaders.contains(lowerCaseKey) == false {
continue
}
if lowerCaseKey == "content-length" && value == "0" {
continue
}
headers[lowerCaseKey] = value;
}
// Sort the header keys alphabetically
let headerKeys = headers.keys.sorted()
// Create a string of all headers, arranged alphabetically, seperated by newlines
var headerString = ""
for (index, key) in headerKeys.enumerated() {
guard let value = headers[key] else {
continue
}
headerString += "(key):(value)"
if index != headerKeys.count - 1 {
headerString += "n"
}
}
// Hash the data payload
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes { bytes in
_ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest)
}
let dataHash = Data(digest).map { String(format: "%02hhx", $0) }.joined()
// Combine all components of this request into a string
let components: [String] = [method, path, queryString, headerString, dataHash]
return components.joined(separator: "n");
}
/// Generate a HMAC hash for a canonicalized request
/// - Parameters:
/// - canonicalized: canonicalized version of a request
/// - secret: Secret key
/// - algorithm: Algorithm to use to generate the hmac
/// - Throws: If algorithm is not supported
/// - Returns: Signature for the request
internal func signature(canonicalized: String, secret: String, algorithm: HMACAlgorithm) throws -> String {
let supportedAlgorithms = ["sha1": (kCCHmacAlgSHA1, CC_SHA1_DIGEST_LENGTH),
"sha256": (kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH),
"sha512": (kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH)]
guard let (algorithmKey, digestLength) = supportedAlgorithms[algorithm.rawValue] else {
throw RequestSigningError.invalidAlgorithm
}
var digest = [UInt8](repeating: 0, count: Int(digestLength))
CCHmac(CCHmacAlgorithm(algorithmKey), secret, secret.count, canonicalized, canonicalized.count, &digest)
let data = Data(digest)
return data.map { String(format: "%02hhx", $0) }.joined()
}
}
错误代码APIG.303适用于:不正确的应用程序身份验证信息,通常是不正确的程序身份验证。在您的情况下,可能是授权格式不正确
请检查请求方法、路径、查询字符串、请求正文是否与签名时使用的一致;检查客户的日期和时间是否正确;并通过参考Calling API Through App Authentication来检查签名代码是否正确。
以下是详细的API网关用户指南,供您参考:https://support.huaweicloud.com/intl/en-us/ae-ad-1-usermanual-apig/ae-APIG-usermanual.pdf
以下是签名验证的两个例子,看看它们是否能给你一些关于错误发生原因的想法:
private static finà Pattern authorizationPattern = Pattern.compile("SDK-HMAC-SHA256\s+Access=([^,]+),
s?SignedHeaders=([^,]+),\s?Signature=(\w+)");
...
String authorization = request.getHeader("Authorization");
if (authorization == null || authorization.length() == 0) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization not found.");
return;
}
Matcher m = authorizationPattern.matcher(authorization);
if (Ȃmȇfin()) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization format incorrect.");
return;
}
String signingKey = m.group(1);
String signingSecret = secrets.get(signingKey);
if (signingSecret == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signing key not found.");
return;
}
String[] signedHeaders = m.group(2).split(";")
;
另一个:
if "authorization" not in request.headers:
return 'Authorization not found.', 401
authorization = request.headers['authorization']
m = authorizationPattern.match(authorization)
if m is None:
return 'Authorization format incorrect.', 401
signingKey = m.group(1)
signedHeaders = m.group(2).split(";")if "authorization" not in request.headers:
return 'Authorization not found.', 401
authorization = request.headers['authorization']
m = authorizationPattern.match(authorization)
if m is None:
return 'Authorization format incorrect.', 401
signingKey = m.group(1)
signedHeaders = m.group(2).split(";")