UNPKG

qiniu-auth

Version:

为七牛云操作提供加密支持

147 lines (125 loc) 6.71 kB
const hmac_sha1 = require('./hmac_sha1'); const urlsafe_base64_encode = require('./urlsafe_base64_encode'); /** * API 签名 * 官方文档:https://developer.qiniu.com/insight/api/4814/the-api-signature * 签名是七牛服务器用来识别用户身份与权限的凭证,我们采用 AK/SK(公钥/私钥)、token 两种方式来对用户进行身份验证。 * 默认使用token签名 * @param {string} method 必选,请求的方法 * @param {string} path 必选,请求的路径 * @param {object} query 可选,请求的参数 * @param {object} headers 可选,请求头信息 * @param {object} expires 可选,过期时间,默认是一个小时 * @param {string} options.AccessKey 可选,七牛云给你的密钥之一,this.AccessKey和options.AccessKey必须要选一 * @param {string} options.SecretKey 可选,七牛云给你的密钥之一,this.SecretKey和options.SecretKey必须要选一 */ module.exports = function (options){ let { AccessKey, SecretKey, method, path, query, headers, expires } = options; // 优先options参数中的AccessKey和SecretKey AccessKey = AccessKey || this.AccessKey; SecretKey = SecretKey || this.SecretKey; // 参数校验 if (!AccessKey || !SecretKey || !method || !path) { throw new Error('token签名出错:AccessKey、SecretKey、method、path4个参数都是必选'); } // headers默认是空对象,expires默认是一个小时 headers = headers || {}; expires = expires || Math.floor(Date.now() / 1000) + 3600; // 1.构造tokenDescription let tokenDescription = JSON.stringify({ resource: CanonicalizedResource(path, query), expires: expires, contentMD5: headers['content-md5'] || headers['Content-Md5'] || '', contentType: headers['content-type'] || headers['Content-Type'] || '', headers: CanonicalizedQiniuHeaders(headers), method: method }); // 2. 将tokenDescription进行URL安全的Base64编码,得到encodedTokenDescription let encodedTokenDescription = urlsafe_base64_encode(tokenDescription); // 3. 使用SecretKey对encodedTokenDescription计算HMAC-SHA1签名 let sign = hmac_sha1(SecretKey, encodedTokenDescription); // 4.对sign进行URL安全的Base64编码 let encodedSign = urlsafe_base64_encode(sign); let auth = AccessKey + ':' + encodedSign + ':' + encodedTokenDescription; return 'Pandora ' + auth; } /** * AK/SK 签名 * @param {string} method 必选,请求的方法 * @param {string} path 必选,请求的路径 * @param {object} query 可选,请求的参数 * @param {object} headers 可选,请求头信息 * @param {string} options.AccessKey 可选,七牛云给你的密钥之一,this.AccessKey和options.AccessKey必须要选一 * @param {string} options.SecretKey 可选,七牛云给你的密钥之一,this.SecretKey和options.SecretKey必须要选一 */ module.exports.AK_SK = function (options){ let { AccessKey, SecretKey, method, headers, path, query } = options; // 优先options参数中的AccessKey和SecretKey AccessKey = AccessKey || this.AccessKey; SecretKey = SecretKey || this.SecretKey; if (!AccessKey || !SecretKey || !method || !path) { throw new Error('AK/SK 签名出错:AccessKey、SecretKey、method、path4个参数都是必选'); } // headers默认是空对象,expires默认是一个小时 headers = headers || {}; headers['date'] = new Date().toGMTString(); // - 制作 AK/SK 签名 // 注意1:签名字符串中的 content-md5 和 content-type 为空那么相应的位置用空字符串来占位。 // 注意2: Date 参数与服务器时间的偏差不得超过 15 分钟,用户需要同步校准自己的时钟。 // 注意3:频繁返回 401 状态码时请先检查 Date 相关的代码逻辑。 // 1. 生成待签名的原始字符串 let strToSign = method + '\n' + (headers['content-md5'] || headers['Content-Md5'] || '') + '\n' + (headers['content-type'] || headers['Content-Type'] || '') + '\n' + headers['date'] + '\n' + CanonicalizedQiniuHeaders(headers) + CanonicalizedResource(path, query); // 2. 使用 SecretKey 对上一步生成的 strTosign 计算 HMAC-SHA1 签名 let sign = hmac_sha1(SecretKey, strToSign); // 3. 对 sign 进行 URL 安全的 Base64 编码 let encodedSign = urlsafe_base64_encode(sign); // 4. 将 AccessKey 和 encodedSign 用英文符号:连接起来 let auth = AccessKey + ':' + encodedSign; return 'Pandora ' + auth; } /** * CanonicalizedQiniuHeaders计算 * 官方文档:https://developer.qiniu.com/insight/api/4814/the-api-signature#canonicalizedqiniuheaders- * 以X-Qiniu-开头的header是七牛的服务自定义的头部,有其特殊意义,因此签名中也需要加进去所有的自定义头部,CanonicalizedQiniuHeaders的计算步骤如下: * 1.将所有以X-Qiniu-为前缀的HTTP请求头的名字转换成小写字母,例如X-Qiniu-pipeline-timeout: 20转换成x-qiniu-pipeline-timeout:20; * 2.将上一步得到的所有的HTTP请求头按照名字的字典序进行升序排列; * 3.删除请求头和内容之间分隔符两端出现的空格; * 4.将每一个头和内容用\n分隔符分隔拼成最后的CanonicalizedQiniuHeaders。 * 注意:当不存在Qiniu headers的时候无需添加最后的换行符。 */ function CanonicalizedQiniuHeaders(headers){ let result = []; for (let key in headers) { if (/^X-Qiniu-(.+)$/.test(key)) { result.push(key.toLowerCase() + ':' + headers[key]); } } // 如果没有X-Qiniu-自定义参数直接返回 if (result.length === 0) return ''; // 当不存在Qiniu headers的时候无需添加最后的换行符 return result.sort().join('\n') + '\n'; } /** * CanonicalizedResource计算 * 官方文档:https://developer.qiniu.com/insight/api/4814/the-api-signature#canonicalizedresource- * 1.将CanonicalizedResource置为空字符串(""); * 2.将请求的pipeline资源的uri放入CanonicalizedResource,例如/v2/repos/repox/exports/exportx; * 3.如果请求的资源包含了子资源,那么将子资源按照字典序升序排列并以&为分隔符生成子资源,以?为分割符追加在CanonicalizedResource字符串的后面,例如/v2/repos/repos/repox?q1=v1&q2=v2; */ function CanonicalizedResource(path, query){ if (!query) return path; // 构建query let query_arr = []; for (let key in query) { query_arr.push(key + '=' + query[key]); } // query_arr没有值,直接返回 if (query_arr.length === 0) return path; // query_arr排序后拼接 return path + '?' + query_arr.sort().join('&'); }