{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"90390aa1-658a-4be0-bb73-4ceb35eae9d3","name":"华筑通开放平台","description":"# API鉴权\n\n客户端涉及的AK/SK签名以及请求发送的流程概述如下：\n\n1. 构造规范请求。\n2. 将待发送的请求内容按照与API网关后台约定的规则组装，确保客户端签名、API网关后台认证时使用的请求内容一致。\n3. 使用规范请求和其他信息创建待签字符串。\n4. 使用AK/SK和待签字符串计算签名。\n5. 将生成的签名信息作为请求消息头添加到HTTP请求中，或者作为查询字符串参数添加到HTTP请求中。\n\n以下做详细介绍。\n\n## 步骤1：构造规范请求\n\n使用AK/SK方式进行签名与认证，首先需要规范请求内容，然后再进行签名。客户端与云服务API网关使用相同的请求规范，可以确保同一个HTTP请求的前后端得到相同的签名结果，从而完成身份校验。\n\nHTTP请求规范伪代码如下：\n\n```\nCanonicalRequest =\n      HTTPRequestMethod + '\\n' +\n      CanonicalURI + '\\n' +\n      CanonicalQueryString + '\\n' +\n      CanonicalHeaders + '\\n' +\n      SignedHeaders + '\\n' +\n      HexEncode(Hash(RequestPayload))\n```\n\n假设原始请求：\n\n```\nGET https://service.region.example.com/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs?limit=2&marker=13551d6b-755d-4757-b956-536f674975c0 HTTP/1.1\nHost: service.region.example.com\n```\n\n1. 构造HTTP请求方法（HTTPRequestMethod），以换行符结束。\n\n   HTTP请求方法，如GET、PUT、POST等。请求方法示例：\n   \n   ```\n   GET\n   ```\n   \n2. 添加规范**URI参数（CanonicalURI）**，以换行符结束。\n\n\t**释义：**\n\n\t规范URI，即请求资源路径，是URI的绝对路径部分的URI编码。\n\t\n\t**格式：**\n\t\n\t根据RFC 3986标准化URI路径，移除冗余和相对路径部分，路径中每个部分必须为URI编码。如果URI路径不以“/”结尾，则在尾部添加“/”。\n\t\n\t**举例：**\n\t\n\tURI具体见云服务的API参考手册，每个API章节都提供对应的资源路径。如虚拟私有云服务的查询VPC列表：/v1/{project_id}/vpcs，此时规范的URI编码为：\n\n\t```\n\tGET\n\t/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs/\n\t```\n\t\n\t> 说明：\n\t>\n\t> 计算签名时，URI必须以“/”结尾。发送请求时，可以不以“/”结尾。\n\t\n3. 添加规范查询字符串（CanonicalQueryString），以换行符结束。\n\t\n\t**释义：**\n\t\n\t查询字符串，即查询参数。如果没有查询参数，则为空字符串，即规范后的请求为空行。\n\t\n\t**格式：**\n\t\n\t规范查询字符串需要满足以下要求：\n\t\n\t* 根据以下规则对每个参数名和值进行URI编码：\n\t\n\t\t* 请勿对RFC 3986定义的任何非预留字符进行URI编码，这些字符包括：A-Z、a-z、0-9、-、_、.和~。\n\t\t* 使用%XY对所有非预留字符进行百分比编码，其中X和Y为十六进制字符（0-9和A-F）。例如，空格字符必须编码为%20，扩展UTF-8字符必须采用“%XY%ZA%BC”格式。\n\n\t* 对于每个参数，追加“URI编码的参数名称=URI编码的参数值”。如果没有参数值，则以空字符串代替，但不能省略“=”。\n\t  例如以下含有两个参数，其中第二个参数parm2的值为空。\n\t\n\t\t```\n\t\tparm1=value1&parm2=\n\t\t```\n\t\t\n\t* 按照字符代码以升序顺序对参数名进行排序。例如，以大写字母F开头的参数名排在以小写字母b开头的参数名之前。\n\t* 以排序后的第一个参数名开始，构造规范查询字符串。\n\t\n\t**举例：**\n\t\n\t查询VPC列表有两个可选参数：limit（每页返回的个数）、marker（分页查询的起始VPC资源ID）\n\t\n\t```\n\tGET\n\t/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs/\n\tlimit=2&marker=13551d6b-755d-4757-b956-536f674975c0\n\t```\n\n4. 添加规范消息头（CanonicalHeaders），以换行符结束。\n\n\t**释义：**\n\t\n\t规范消息头，即请求消息头列表。包括签名请求中的所有HTTP消息头列表。消息头必须包含X-Sdk-Date，用于校验签名时间，格式为ISO8601标准的UTC时间格式：YYYYMMDDTHHMMSSZ。\n\t\n\t**格式：**\n\t\n\tCanonicalHeaders由多个请求消息头共同组成，**CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ...，**其中每个请求消息头（CanonicalHeadersEntry ）的格式为**Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + '\\n'**\n\n\t> \t说明：\n\t> \t\n\t> \t* Lowercase表示将所有字符转换为小写字母的函数。\n\t> \t\n\t> \t* Trimall表示删除值前后的多余空格的函数。\n\t> \t\n\t> \t* 最后一个请求消息头也会携带一个换行符。叠加规范中CanonicalHeaders自身携带的换行符，因此会出现一个空行。\n\n\t**举例：**\n\t\n\t查询VPC列表的消息头，需要包含签名时间（X-Sdk-Date），云服务Endpoint（Host）、内容类型（Content-Type）。\n\t\n\t```\n\tGET\n\t/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs/\n\tlimit=2&marker=13551d6b-755d-4757-b956-536f674975c0\n\tcontent-type:application/json\n\thost:service.region.example.com\n\tx-sdk-date:20190329T074551Z\n\t```\n\t\n\t> **注意：**\n\t> \t\n\t> 规范消息头需要满足以下要求：\n\t> \t\n\t> * 将消息头名称转换为小写形式，并删除前导空格和尾随空格。\n\t> \t\n\t> * 按照字符代码对消息头名称进行升序排序。\n\t> \t\n\t> 例如原始消息头为：\n\t> \t\n\t> ```\n\t> Host: service.region.example.com\\n\n\t> Content-Type: application/json;charset=utf8\\n\n\t> My-header1:    a   b   c  \\n\n\t> X-Sdk-Date:20190318T094751Z\\n\n\t> My-Header2:    \"x   y   \\n\n\t> ```\n\t> \t\n\t> 对消息头名称转小写，按消息头名称字符代码对消息头排序，将消息头的值去掉前导空格与尾随空格。最终得到规范消息头：\n\t> \t\n\t> ```\n\t> content-type:application/json;charset=utf8\\n\n\t> host:service.region.example.com\\n\n\t> my-header1:a   b   c\\n\n\t> my-header2:\"x   y\\n\n\t> x-sdk-date:20190318T094751Z\\n\n\t> ```\n\n5. 添加用于签名的消息头声明（SignedHeaders），以换行符结束。\n\t\n\t**释义：**\n\t\n\t用于签名的请求消息头列表。通过添加此消息头，向API网关告知请求中哪些消息头是签名过程的一部分，以及在验证请求时API网关可以忽略哪些消息头。X-Sdk-date必须作为已签名的消息头。\n\t\n\t**格式：**\n\t\n\tSignedHeaders = Lowercase(HeaderName0) + ';' + Lowercase(HeaderName1) + \";\" + ...\n\t\n\t已签名的消息头需要满足以下要求：将已签名的消息头名称转换为小写形式，按照字符代码对消息头进行排序，并使用“;”来分隔多个消息头。\n\t\n\tLowercase表示将所有字符转换为小写字母。\n\t\n\t**举例：**\n\t\n\t以下表示有三个消息头参与签名：Content-Type、Host、X-Sdk-Date\n\t\n\t```\n\tGET\n\t/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs/\n\tlimit=2&marker=13551d6b-755d-4757-b956-536f674975c0\n\tcontent-type:application/json\n\thost:service.region.example.com\n\tx-sdk-date:20190329T074551Z\n\t\n\tcontent-type;host;x-sdk-date\n\t```\n\t\n6. 使用SHA 256哈希函数以基于HTTP或HTTPS请求正文中的body体（RequestPayload），创建哈希值。\n\t\n\t**释义：**\n\t\n\t请求消息体。消息体需要做两层转换：HexEncode(Hash(RequestPayload))，其中Hash表示生成消息摘要的函数，当前支持SHA-256算法。HexEncode表示以小写字母形式返回摘要的Base-16编码的函数。例如，HexEncode(\"m\") 返回值为“6d”而不是“6D”。输入的每一个字节都表示为两个十六进制字符。\n\t\n\t**说明：**\n\t\n\t计算RequestPayload的哈希值时，对于“RequestPayload==null”的场景，直接使用空字符串\"\"来计算。\n\t\n\t**举例：**\n\t\n\t本示例为GET方法，body体为空。经过哈希处理的body（空字符串）如下：\n\t\n\t```\n\tGET\n\t/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs/\n\tlimit=2&marker=13551d6b-755d-4757-b956-536f674975c0\n\tcontent-type:application/json\n\thost:service.region.example.com\n\tx-sdk-date:20190329T074551Z\n\t\n\tcontent-type;host;x-sdk-date\n\te3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n\t```\n\t\n\t至此，规范请求构造完成。\n\t\n7. 对构造好的规范请求进行哈希处理，算法与对RequestPayload哈希处理的算法相同。经过哈希处理的规范请求必须以小写十六进制字符串形式表示。\n\t\n\t算法伪代码：**Lowercase(HexEncode(Hash.SHA256(CanonicalRequest)))**\n\t\n\t经过哈希处理的规范请求示例：\n\t\n\t```\n\t9f5ad2be0a6921a5ea888f13f3e1a750da9c45e6978812ffafc140bdecba1174\n\t```\n\n## 步骤2：创建待签字符串\n\n对HTTP请求进行规范并取得请求的哈希值后，将其与签名算法、签名时间一起组成待签名字符串。\n\n```\nStringToSign =\n    Algorithm + \\n +\n    RequestDateTime + \\n +\n    HashedCanonicalRequest\n```\n\n伪代码中参数说明如下。\n\n* **Algorithm**\n\n\t签名算法。对于SHA 256，算法为SDK-HMAC-SHA256。\n\n* **RequestDateTime**\n\n\t请求时间戳。与请求消息头X-Sdk-Date的值相同，格式为YYYYMMDDTHHMMSSZ。\n\n* **HashedCanonicalRequest**\n\n\t经过哈希处理的规范请求。\n\n上述例子得到的待签字符串为：\n\n```\nSDK-HMAC-SHA256\n20190329T074551Z\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n```\n\n## 步骤3：计算签名\n\n将SK（Access Secret Key）和创建的待签字符串作为加密哈希函数的输入，计算签名，将二进制值转换为十六进制表示形式。\n\n伪代码如下：\n\n```\nsignature = HexEncode(HMAC(Access Secret Key, string to sign))\n```\n\n其中HMAC指密钥相关的哈希运算，HexEncode指转十六进制。伪代码中参数说明如表1所示。\n\n表1 参数说明\n\n参数名称 | 参数解释\n---|---\nAccess Secret Key | 签名密钥\nstring to sign | 创建的待签字符串\n\n假设Access Secret Key为MFyfvK41ba2giqM7Uio6PznpdUKGpownRZlmVmHc，则计算得到的signature为：\n\n```\n9f5ad2be0a6921a5ea888f13f3e1a750da9c45e6978812ffafc140bdecba1174\n```\n\n## 步骤4：添加签名信息到请求头\n\n在计算签名后，将它添加到Authorization的HTTP消息头。Authorization消息头未包含在已签名消息头中，主要用于身份验证。\n\n伪代码如下：\n\n```\nAuthorization header创建伪码：\nAuthorization: algorithm Access=Access key, SignedHeaders=SignedHeaders, Signature=signature\n```\n\n需要注意的是算法与Access之前有空格但没有逗号，但是SignedHeaders与Signature之前需要使用逗号隔开。\n\n得到的签名消息头为：\n\n```\nSDK-HMAC-SHA256 Access=QTWAOYTTINDUT2QVKYUC, SignedHeaders=content-type;host;x-sdk-date, Signature=d66f6a6c536e984129e13a4060f465225909fd126d212cb25e9e292346aae036\n```\n\n得到签名消息头后，将其增加到原始HTTP请求内容中，请求将被发送给云服务API网关，由API网关完成身份认证。身份认证通过后，该请求才会发送给具体的云服务进行业务处理。\n\n包含签名信息的完整请求如下：\n\n```\nGET /v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs?limit=2&amp; marker=13551d6b-755d-4757-b956-536f674975c0 HTTP/1.1\nHost: service.region.example.com\nContent-Type: application/json\nx-sdk-date: 20190329T074551Z\nAuthorization: SDK-HMAC-SHA256 Access=QTWAOYTTINDUT2QVKYUC, SignedHeaders=content-type;host;x-sdk-date, Signature=d66f6a6c536e984129e13a4060f465225909fd126d212cb25e9e292346aae036\n```\n\nCurl方式样例如下：\n\n```\ncurl -X GET \"https://service.region.example.com/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs?limit=2&marker=13551d6b-755d-4757-b956-536f674975c0\" -H \"Content-Type: application/json\" -H \"X-Sdk-Date: 20190329T074551Z\" -H \"host: service.region.example.com\" -H \"Authorization: SDK-HMAC-SHA256 Access=QTWAOYTTINDUT2QVKYUC, SignedHeaders=content-type;host;x-sdk-date, Signature=d66f6a6c536e984129e13a4060f465225909fd126d212cb25e9e292346aae036\" -d $''\n```","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"1365259","team":119449,"collectionId":"90390aa1-658a-4be0-bb73-4ceb35eae9d3","publishedId":"SVYqNeNM","public":true,"publicUrl":"https://doc.api.huazhutong.com","privateUrl":"https://go.postman.co/documentation/1365259-90390aa1-658a-4be0-bb73-4ceb35eae9d3","customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"2F54EB"},"documentationLayout":"classic-double-column","version":"8.10.1","publishDate":"2019-08-04T08:53:10.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{},"logos":{}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/da90e717fcccaa26713d93372fbe05681b38b9d5a171acd9bda16f618a1a3523","favicon":"https://res.cloudinary.com/postman/image/upload/v1564906541/team/ae6sgt38fc4i8sthi6jt.ico"},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://doc.api.huazhutong.com/view/metadata/SVYqNeNM"}