背景
由于项目需要,微服务的API都是从AWS API Gateway发布的,然后给第三方系统去调用,比如小程序等。
AWS提供了很详细的文档来生成签名,同时也可以生成各种编程语言的SDK来简化签名。
本文记录了使用nodejs如何一步一步去实现这个验签过程的。
API Gateway方法请求保护
AWS API Gateway是AWS的一项全托管服务,可以用来发布管理保护监控API。保护API通常在方法请求中设置两种验证方式:
- AWS_IAM
- API Key
具体设置如下:

API Key验证
这个非常简单,只需要在请求的http header里面添加一行
x-api-key: <your api key>
AWS_IAM 计算签名
API Gateway支持AWS Signature V4, 简单来讲就是对于向AWS发起的每个http请求都集成AKSK作为安全凭证。
计算签名步骤一共有4步:
- 创建规范请求
- 创建签名字符串
- 计算签名
- 添加签名到http header
创建规范请求
这里需要注意的是对于payload生成一个hash值。使用SHA256指定AWS4-HMAC-SHA256 作为签名算法。经过哈希处理的负载必须以小写十六进制(hex)字符串形式表示。
const hash = (string, encoding) => {
return crypto
.createHash('sha256')
.update(string, 'utf8')
.digest(encoding);
};
另外需要注意的是在signed header前需要添加一个\n换行符
const createCanonicalRequest = request => {
const canonicalRequest = [
request.method,
request.path,
request.query,
canonicalHeaders(request.headers) + '\n',
signedHeaders(request.headers),
hash(request.data, 'hex')
].join('\n');
console.log('========Canonical Request============');
console.log(canonicalRequest);
console.log('========Canonical Request============\n');
return canonicalRequest;
};
创建签名字符串
这一步需要注意的是x-amz-date时间是UTC时间。
const createStringToSign = (options, request) => {
const stringToSign = [
'AWS4-HMAC-SHA256',
getDateTime(),
getDate() + `/${options.region}/${options.service}/aws4_request`,
hash(createCanonicalRequest(request), 'hex')
].join('\n');
console.log('========String to sign============');
console.log(stringToSign);
console.log('========String to sign============\n');
return stringToSign;
};
计算签名
根据文档的伪代码去嵌套计算签名,时间仍然为UTC时间。
kSecret = your secret access key
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")
const calculateSignature = (options, request) => {
const kSecret = options.secretAccessKey;
const kDate = hmac('AWS4' + kSecret, getDate());
const kRegion = hmac(kDate, options.region);
const kService = hmac(kRegion, options.service);
const kSigning = hmac(kService, 'aws4_request');
const singature = hmac(kSigning, createStringToSign(options, request), 'hex');
console.log('========Authorization============');
console.log(singature);
console.log('========Authorization============\n');
return singature;
};
添加签名
const calculateAuthorization = (options, request) => {
return `AWS4-HMAC-SHA256 Credential=${options.accessKeyId}/${getDate()}/${
options.region
}/${options.service}/aws4_request, SignedHeaders=${signedHeaders(
request.headers
)}, Signature=${calculateSignature(options, request)}`;
};
