ny
昨天 282fbc6488f4e8ceb5fda759f963ee88fbf7b999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
<script lang="ts" setup>
import { BasicModal, useModalInner } from '@jnpf/ui/modal';
 
const [registerModal] = useModalInner();
</script>
<template>
  <BasicModal v-bind="$attrs" @register="registerModal" title="验证签名使用说明" :footer="null" :width="800">
    <div class="doc-container">
      <div class="doc-container-main">
        <h4>验证开启</h4>
        <p>验证签名关闭,不使用验证机制</p>
        <p>直接将appId和appSecret,按照 Authorization: appId::appSecret 格式加入到 Header 中。</p>
        <p>验证签名开启,使用下列签名验证机制</p>
        <h4>签名验证机制</h4>
        <p>和第三方服务对接时,需要建立通讯双方的信任关系,以及确保通讯内容不被篡改,不被重放等。通常,我们采用 HTTPS 和 KeyId/SecretKey 的方式:</p>
        <ol>
          <li>HTTPS 用来确定接收方的身份,利用公钥证书机制来保证。</li>
          <li>
            根据双方共享的一对 keyId/secret,使用 HMAC
            签名机制,发送方对请求内容进行签名,并加入到Header中,接收方使用同样的方式对请求进行签名计算,比对签名结果用来判别发送方的身份。
          </li>
        </ol>
        <p>当前HTTP授权证书支持globalsign, symantec, geotrust, comodo这四类,其余证书类型需要经过处理,暂时不推荐。</p>
        <p>本系统内采用appId/appSecret来进行签名。</p>
        <h4>签名过程</h4>
        <p>签名分为以下4个步骤:</p>
        <ol>
          <li>选择合适的签名方法</li>
          <li>根据对应的签名,构建待签名的字符串</li>
          <li>根据对应的签名算法和对应的appSecret,计算待签名字符串的 Hmac 签名</li>
          <li>
            将签名按规定格式加入到 Header: Authorization中。
            <p>通常使用: Authorization: SignVersion:KeyId:Scope:Signature;</p>
          </li>
          <p>本系统内最终以KeyId:Signature规则展示</p>
        </ol>
        <h4>签名方法</h4>
        <p>为了使双方采用一致的算法计算签名,需要规定签名字符串的构建和签名算法,为了满足后续的签名算法升级,当前支持的签名方式为: HmacSHA256</p>
        <p>待签名字符串,请按照以下顺序构建</p>
        <ol>
          <li><p>Http Method, 大写。例如 GET, POST。加上换行符 \n</p></li>
          <li><p>URI Path。query params string 之前的部分。 加上换行符 \n</p></li>
          <li>
            <p>Header 中的 YmDate, 以时间戳的形式,例如(2022-06-28 16:26:11=>1656404771000)。加上换行符 \n</p>
            <p>该时间将会用来判断请求有效性,限定一分钟之内有效。若无效返回验证超时。</p>
          </li>
          <li><p>UserKey用于用户认证 \n</p></li>
          <li><p>Header 中的 Host,为开发者自身的域名,加上换行符 \n</p></li>
        </ol>
        <p>Header规范化方法如下:</p>
        <ol>
          <li>将所有的 header name 都变成小写,按照字典序排序。</li>
          <li>对每一个小写的 name, 取出所有的对应的 value。如果有多个 value, 对所有的 value 进行字典序排序,用逗号 (,) 连接。</li>
          <li>对每一对 name, value。按照如下方式拼接:name:value\n (\n 为换行)</li>
        </ol>
        <h4>计算待签名字符串的签名</h4>
        <p><b>签名使用的 Key</b></p>
        <ol>
          <li>HmacSHA256 方式直接使用 appId 对应的 appSecret。通常这是一个32位 Base64 字符串,将其 Base64 decode 为字节数组。</li>
          <li>其他方法,为了更加安全的保护 appSecret, 可能会使用特定的算法根据 appSecret 派生出新的签名用的 key。目前保留。</li>
        </ol>
        <p><b>计算签名</b></p>
        <ol>
          <li>使用 HmacSha256 计算 Hmac 签名。</li>
          <li>将待签名字符串,获取对应的 UTF-8 字节数组。</li>
          <li>使用签名算法对待签名字节数组计算 Hmac 签名。</li>
          <li>将签名结果变成 Hex 字符串。</li>
          <li>将签名结果,按照 Authorization: &lt;KeyId&gt;:&lt;Signature&gt; 格式加入到 Header 中。</li>
        </ol>
        <h4>签名示例</h4>
        <p>针对 appId, appSecret (abcde, xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy)</p>
        <p>appId和appSecret为必传参数。可以添加为请求头参数(Header)如:abcde:xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy</p>
        <p>其他参数:如果是GET请求,统一路径传参;如果是POST请求,统一Body-application/json传参。</p>
        <h5>GET 请求:</h5>
        <p>
          <em>GET</em>
          /api/system/DataInterface/{id}/Actions/Response?tenantId=xxxxx&amp;name=abc<br />
          Host : localhost:30000<br />
          YmDate : 1656404771000<br />
          UserKey : xxxxxxx<br />
        </p>
        <p>按照规则生成的待签名字符串如下:</p>
        <table class="table-bordered table">
          <thead>
            <tr>
              <th>签名结果</th>
              <th>说明</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>待签名字段名</td>
              <td>待签名字段值</td>
            </tr>
            <tr>
              <td>Http Method</td>
              <td>GET</td>
            </tr>
            <tr>
              <td>URL Path</td>
              <td>/api/system/DataInterface/{id}/Actions/Response</td>
            </tr>
            <tr>
              <td>YmDate</td>
              <td>1656404771000</td>
            </tr>
            <tr>
              <td>UserKey</td>
              <td>xxxxxxx</td>
            </tr>
            <tr>
              <td>Host</td>
              <td>localhost:30000</td>
            </tr>
            <tr>
              <td>最终待签名字符串</td>
              <td>以下 \n 表明换行<br />GET\n<br />/api/system/DataInterface/{id}/Actions/Response\n<br />1656404771000\n <br />localhost:30000\n<br /></td>
            </tr>
            <tr>
              <td>最终计算的 Authorization header</td>
              <td>Authorization: abcde::5a13e56353da7954a96605c7f21f0f14a26a49baf387f95e600cdbdd7f0dc604</td>
            </tr>
          </tbody>
        </table>
        <h5>POST 请求:</h5>
        <p>
          <em>
            POST /api/system/DataInterface/{id}/Actions/Response<br />
            Host : localhost:30000<br />
            YmDate : 1656404771000<br />
            UserKey : xxxxxxx<br />
            body参数json格式如:{"tenantId":"123","name":"abc"}
          </em>
        </p>
        <p>按照规则生成的待签名字符串如下:</p>
        <table class="table-bordered table">
          <thead>
            <tr>
              <th>签名结果</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>待签名字段名</td>
              <td>待签名字段值</td>
            </tr>
            <tr>
              <td>Http Method</td>
              <td>POST</td>
            </tr>
            <tr>
              <td>URL Path</td>
              <td>/api/system/DataInterface/{id}/Actions/Response</td>
            </tr>
            <tr>
              <td>YmDate</td>
              <td>1656404771000</td>
            </tr>
            <tr>
              <td>UserKey</td>
              <td>xxxxxxx</td>
            </tr>
            <tr>
              <td>Host</td>
              <td>localhost:30000</td>
            </tr>
            <tr>
              <td>最终待签名字符串</td>
              <td>以下 \n 表明换行<br />POST\n<br />/hmac/testPost\n<br />1656404771000\n <br />localhost:30000\n<br /></td>
            </tr>
            <tr>
              <td>最终计算的 Authorization header</td>
              <td>Authorization : abcde::7af664ac15b675633bc13a3ab03adc2d0c6e3834999785698e5830ae215a8545</td>
            </tr>
          </tbody>
        </table>
        <h4>(java版)生成待签名字符串及Authorization代码示例:</h4>
        <pre
          class="language-java"><code class=" language-java"><span class="token keyword">import</span> org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>commons<span class="token punctuation">.</span>codec<span class="token punctuation">.</span>binary<span class="token punctuation">.</span>Base64<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>commons<span class="token punctuation">.</span>codec<span class="token punctuation">.</span>binary<span class="token punctuation">.</span>Hex<span class="token punctuation">;</span>
<span class="token keyword">import</span> javax<span class="token punctuation">.</span>crypto<span class="token punctuation">.</span>Mac<span class="token punctuation">;</span>
<span class="token keyword">import</span> javax<span class="token punctuation">.</span>crypto<span class="token punctuation">.</span>spec<span class="token punctuation">.</span>SecretKeySpec<span class="token punctuation">;</span>
<span class="token keyword">import</span> java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>UnsupportedEncodingException<span class="token punctuation">;</span>
<span class="token keyword">import</span> java<span class="token punctuation">.</span>security<span class="token punctuation">.</span>InvalidKeyException<span class="token punctuation">;</span>
<span class="token keyword">import</span> java<span class="token punctuation">.</span>security<span class="token punctuation">.</span>NoSuchAlgorithmException<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EncryDemo</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> NoSuchAlgorithmException<span class="token punctuation">,</span> InvalidKeyException<span class="token punctuation">,</span> UnsupportedEncodingException <span class="token punctuation">{</span>
        String secret <span class="token operator">=</span> <span class="token string">"xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy"</span><span class="token punctuation">;</span>
        String algorithmForMac <span class="token operator">=</span> <span class="token string">"HmacSHA256"</span><span class="token punctuation">;</span>
        String method <span class="token operator">=</span> <span class="token string">"POST"</span><span class="token punctuation">;</span>
        String urlPath <span class="token operator">=</span> <span class="token string">"/api/system/DataInterface/{id}/Actions/Response"</span><span class="token punctuation">;</span>
        String YmDate <span class="token operator">=</span> <span class="token string">"1656404771000"</span><span class="token punctuation">;</span>
        String host <span class="token operator">=</span> <span class="token string">"localhost:30000"</span><span class="token punctuation">;</span>
        String source <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder()</span>
                      <span class="token punctuation">.</span><span class="token function">append</span>(method)<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span>
                      <span class="token punctuation">.</span><span class="token function">append</span>(url)<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span>
                      <span class="token punctuation">.</span><span class="token function">append</span>(ymdate)<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span>
                      <span class="token punctuation">.</span><span class="token function">append</span>(host)<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span><span class="token punctuation">.toString();</span>
        Mac mac <span class="token operator">=</span> Mac<span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span>algorithmForMac<span class="token punctuation">)</span><span class="token punctuation">;</span>
        SecretKeySpec secretKeySpec <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SecretKeySpec</span><span class="token punctuation">(</span>Base64<span class="token punctuation">.</span><span class="token function">decodeBase64</span><span class="token punctuation">(</span>secret<span class="token punctuation">)</span><span class="token punctuation">,</span> algorithmForMac<span class="token punctuation">)</span><span class="token punctuation">;</span>
        mac<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span>secretKeySpec<span class="token punctuation">)</span><span class="token punctuation">;</span>
        String signature <span class="token operator">=</span> Hex<span class="token punctuation">.</span><span class="token function">encodeHexString</span><span class="token punctuation">(</span>mac<span class="token punctuation">.</span><span class="token function">doFinal</span><span class="token punctuation">(</span>source<span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token string">"utf-8"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
        <h4>(.net版)生成待签名字符串及Authorization代码示例: ></h4>
        <pre class="language-java"><code class=" language-java"><span class="token keyword">using</span><span> System.Security.Cryptography;</span>
<span class="token keyword">using</span><span> System.Text;</span>
<span class="token keyword">using</span><span> JNPF.Common.Extension;</span>
<span class="token keyword">namespace JNPF.Systems.System</span>
<span class="token punctuation">{</span>
    <span class="token keyword"> internal class Class1</span>
    <span class="token punctuation">{</span>
      <span class="token keyword"> static void Main(string[] args)</span>
      <span class="token punctuation">{</span>
        String secret <span class="token operator">=</span> <span class="token string">"xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy"</span><span class="token punctuation">;</span>
        String method <span class="token operator">=</span> <span class="token string">"POST"</span><span class="token punctuation">;</span>
        String urlPath <span class="token operator">=</span> <span class="token string">"/api/system/DataInterface/{id}/Actions/Response"</span><span class="token punctuation">;</span>
        String YmDate <span class="token operator">=</span> <span class="token string">"1656404771000"</span><span class="token punctuation">;</span>
        String host <span class="token operator">=</span> <span class="token string">"localhost:30000"</span><span class="token punctuation">;</span>
        String source <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder()</span>
                      <span class="token punctuation">.</span><span class="token function">Append</span>(method)<span class="token punctuation">.</span><span class="token function">Append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span>
                      <span class="token punctuation">.</span><span class="token function">Append</span>(url)<span class="token punctuation">.</span><span class="token function">Append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span>
                      <span class="token punctuation">.</span><span class="token function">Append</span>(ymdate)<span class="token punctuation">.</span><span class="token function">Append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span>
                      <span class="token punctuation">.</span><span class="token function">Append</span>(host)<span class="token punctuation">.</span><span class="token function">Append</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span><span class="token punctuation">.ToString();</span>
        <span>using (var hmac = </span><span class="token keyword">new</span><span> HMACSHA256(secret.</span><span class="token function">ToBytes</span><span>(Encoding.UTF8)))</span>
        <span class="token punctuation">{</span>
          <span>byte[] hashmessage = hmac.</span><span class="token function">ComputeHash</span><span>(source.</span><span class="token function">ToBytes</span><span>(Encoding.UTF8));</span>
          <span>var signature = hashmessage.</span><span class="token function">ToHexString</span><span>();</span>
          <span>Console.</span><span class="token function">WriteLine</span><span>(signature);</span>
          <span>Console.</span><span class="token function">ReadKey</span><span>();</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
      </div>
    </div>
  </BasicModal>
</template>
<style lang="scss">
.dark,
.dark[data-theme='custom'],
.dark[data-theme='default'] {
  .doc-container {
    table thead {
      background: #333;
    }
 
    table tbody tr:nth-child(odd) {
      background: #1e1e1e;
    }
 
    table tbody tr:nth-child(2n) {
      background: #333;
    }
 
    code[class*='language-'],
    pre[class*='language-'] {
      font-weight: 400 !important;
      color: transparent !important;
      background: #333 !important;
    }
  }
}
</style>
<style lang="scss" scoped>
table {
  width: 100%;
  margin: 10px 0 !important;
}
 
table thead {
  background: #eceef0;
}
 
table thead tr th {
  font-weight: 600;
}
 
table tbody tr:nth-child(odd) {
  background: #fcfcfc;
}
 
table tbody tr:nth-child(2n) {
  background: #f9f9f9;
}
 
table tr td,
table tr th {
  padding: 17px 20px;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  text-align: left;
}
 
table.noise-table tbody tr td {
  background-color: #f9f9f9;
}
 
.doc-container .doc-container-main ol,
.doc-container .doc-container-main ul {
  padding: 10px 0;
  margin-left: 20px;
  font-size: 16px;
  font-weight: 400;
}
 
.doc-container .doc-container-main ol li,
.doc-container .doc-container-main ul li {
  margin-top: 6px !important;
}
 
.doc-container .doc-container-main ol ol,
.doc-container .doc-container-main ol ul,
.doc-container .doc-container-main ul ol,
.doc-container .doc-container-main ul ul {
  padding: 10px 0 !important;
  margin-left: 0;
}
 
.doc-container .doc-container-main p {
  padding: 6px 0 !important;
  font-size: 16px !important;
  font-weight: 400;
  line-height: 24px;
}
 
.doc-container .doc-container-main table {
  margin: 20px 0;
}
 
.doc-container .doc-container-main h4 {
  padding-top: 10px;
  font-size: 24px !important;
  font-weight: 550;
}
 
.doc-container .doc-container-main h5 {
  padding-top: 20px !important;
  padding-bottom: 10px !important;
  margin: 10px 0 0;
  font-size: 18px;
  font-weight: 550 !important;
  line-height: 28px;
}
 
.doc-container .doc-container-main h5 a {
  font-size: 20px !important;
}
 
.doc-container {
  padding-bottom: 10px;
 
  code[class*='language-'],
  pre[class*='language-'] {
    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    line-height: 1.5;
    color: black;
    text-align: left;
    word-spacing: normal;
    hyphens: none;
    word-break: normal;
    word-wrap: normal;
    tab-size: 4;
    white-space: pre;
    text-shadow: 0 1px white;
    background: none;
  }
 
  pre[class*='language-']::selection,
  pre[class*='language-'] ::selection,
  code[class*='language-']::selection,
  code[class*='language-'] ::selection {
    text-shadow: none;
    background: #b3d4fc;
  }
 
  pre[class*='language-'] {
    padding: 1em;
    margin: 0.5em 0;
    overflow: auto;
  }
 
  :not(pre) > code[class*='language-'],
  pre[class*='language-'] {
    background: #f5f2f0;
  }
 
  :not(pre) > code[class*='language-'] {
    padding: 0.1em;
    white-space: normal;
    border-radius: 0.3em;
  }
}
</style>