@kne/fastify-signature 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/libs/controllers/main.js +45 -14
- package/libs/schema/response.js +39 -0
- package/package.json +1 -1
package/libs/controllers/main.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
const fp = require('fastify-plugin');
|
|
2
|
+
const { str, bool, any, success, empty, pageResult, STATUS, secret } = require('../schema/response');
|
|
3
|
+
|
|
4
|
+
const ADMIN_TAG = '签名管理后台-密钥';
|
|
5
|
+
const TAG = '签名-验签';
|
|
2
6
|
|
|
3
7
|
module.exports = fp(async (fastify, options) => {
|
|
4
8
|
const { services } = fastify[options.name];
|
|
@@ -9,7 +13,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
9
13
|
{
|
|
10
14
|
onRequest: [userAuth, adminAuth],
|
|
11
15
|
schema: {
|
|
12
|
-
|
|
16
|
+
tags: [ADMIN_TAG],
|
|
17
|
+
description: '分页查询全部用户的密钥,按创建时间倒序;密钥只返回脱敏值',
|
|
13
18
|
summary: '获取密钥列表',
|
|
14
19
|
query: {
|
|
15
20
|
type: 'object',
|
|
@@ -27,6 +32,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
27
32
|
filter: {
|
|
28
33
|
type: 'object',
|
|
29
34
|
additionalProperties: true,
|
|
35
|
+
description: '筛选条件',
|
|
30
36
|
properties: {
|
|
31
37
|
keyword: { type: 'string', description: '关键字(AppId/描述/所属用户)' },
|
|
32
38
|
appId: { type: 'string', description: 'AppId' },
|
|
@@ -34,7 +40,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
|
-
}
|
|
43
|
+
},
|
|
44
|
+
response: success('密钥分页列表', pageResult('密钥', secret))
|
|
38
45
|
}
|
|
39
46
|
},
|
|
40
47
|
async request => {
|
|
@@ -47,7 +54,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
47
54
|
{
|
|
48
55
|
onRequest: [userAuth, adminAuth],
|
|
49
56
|
schema: {
|
|
50
|
-
|
|
57
|
+
tags: [ADMIN_TAG],
|
|
58
|
+
description: '生成一对 AppId / 密钥,密钥明文只在创建时返回一次',
|
|
51
59
|
summary: '创建密钥',
|
|
52
60
|
body: {
|
|
53
61
|
type: 'object',
|
|
@@ -55,9 +63,19 @@ module.exports = fp(async (fastify, options) => {
|
|
|
55
63
|
description: {
|
|
56
64
|
type: 'string',
|
|
57
65
|
description: '描述'
|
|
66
|
+
},
|
|
67
|
+
userId: {
|
|
68
|
+
description: '密钥所属用户 ID,不传为当前登录用户;验签通过后以该用户身份访问开放接口'
|
|
58
69
|
}
|
|
59
70
|
}
|
|
60
|
-
}
|
|
71
|
+
},
|
|
72
|
+
response: success('创建的密钥', {
|
|
73
|
+
type: 'object',
|
|
74
|
+
properties: {
|
|
75
|
+
appId: str('AppId'),
|
|
76
|
+
secretKey: str('密钥明文,只返回这一次')
|
|
77
|
+
}
|
|
78
|
+
})
|
|
61
79
|
}
|
|
62
80
|
},
|
|
63
81
|
async request => {
|
|
@@ -70,7 +88,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
70
88
|
{
|
|
71
89
|
onRequest: [userAuth, adminAuth],
|
|
72
90
|
schema: {
|
|
73
|
-
|
|
91
|
+
tags: [ADMIN_TAG],
|
|
92
|
+
description: '删除密钥,删除后该 AppId 无法再通过验签;AppId 不存在时报错',
|
|
74
93
|
summary: '删除密钥',
|
|
75
94
|
body: {
|
|
76
95
|
type: 'object',
|
|
@@ -80,7 +99,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
80
99
|
description: '密钥ID'
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
|
-
}
|
|
102
|
+
},
|
|
103
|
+
response: empty('删除成功')
|
|
84
104
|
}
|
|
85
105
|
},
|
|
86
106
|
async request => {
|
|
@@ -94,7 +114,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
94
114
|
{
|
|
95
115
|
onRequest: [userAuth, adminAuth],
|
|
96
116
|
schema: {
|
|
97
|
-
|
|
117
|
+
tags: [ADMIN_TAG],
|
|
118
|
+
description: '修改密钥状态与描述,未传的字段保持原值;AppId 不存在时报错',
|
|
98
119
|
summary: '更新密钥',
|
|
99
120
|
body: {
|
|
100
121
|
type: 'object',
|
|
@@ -105,14 +126,15 @@ module.exports = fp(async (fastify, options) => {
|
|
|
105
126
|
},
|
|
106
127
|
status: {
|
|
107
128
|
type: 'number',
|
|
108
|
-
description:
|
|
129
|
+
description: `密钥${STATUS}`
|
|
109
130
|
},
|
|
110
131
|
description: {
|
|
111
132
|
type: 'string',
|
|
112
133
|
description: '描述'
|
|
113
134
|
}
|
|
114
135
|
}
|
|
115
|
-
}
|
|
136
|
+
},
|
|
137
|
+
response: empty('更新成功')
|
|
116
138
|
}
|
|
117
139
|
},
|
|
118
140
|
async request => {
|
|
@@ -125,7 +147,8 @@ module.exports = fp(async (fastify, options) => {
|
|
|
125
147
|
`${options.prefix}/verify`,
|
|
126
148
|
{
|
|
127
149
|
schema: {
|
|
128
|
-
|
|
150
|
+
tags: [TAG],
|
|
151
|
+
description: '免登录。按 HMAC-SHA256(`${appId}|${timestamp}|${expire}`, 密钥) 校验签名,通过后记录最后访问时间;签名错误或过期返回 result=false,AppId 不存在时报错',
|
|
129
152
|
summary: '验证密钥',
|
|
130
153
|
body: {
|
|
131
154
|
type: 'object',
|
|
@@ -136,18 +159,26 @@ module.exports = fp(async (fastify, options) => {
|
|
|
136
159
|
},
|
|
137
160
|
timestamp: {
|
|
138
161
|
type: 'number',
|
|
139
|
-
description: '
|
|
162
|
+
description: '签名时的时间戳(秒)'
|
|
140
163
|
},
|
|
141
164
|
signature: {
|
|
142
165
|
type: 'string',
|
|
143
|
-
description: '
|
|
166
|
+
description: '签名(hex)'
|
|
144
167
|
},
|
|
145
168
|
expire: {
|
|
146
169
|
type: 'number',
|
|
147
|
-
description: '
|
|
170
|
+
description: '过期时间戳(秒),当前时间超过即验签失败'
|
|
148
171
|
}
|
|
149
172
|
}
|
|
150
|
-
}
|
|
173
|
+
},
|
|
174
|
+
response: success('验签结果', {
|
|
175
|
+
type: 'object',
|
|
176
|
+
properties: {
|
|
177
|
+
result: bool('是否通过验签'),
|
|
178
|
+
message: str('结果说明'),
|
|
179
|
+
userId: any('密钥所属用户 ID,仅通过验签时返回')
|
|
180
|
+
}
|
|
181
|
+
})
|
|
151
182
|
}
|
|
152
183
|
},
|
|
153
184
|
async request => {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// 响应 schema 会参与 fast-json-stringify 序列化:对象一律 additionalProperties: true 以免裁掉未声明字段;
|
|
2
|
+
// 用 nullable 而非类型数组(类型数组遇到不匹配的值会直接 500),类型不确定的字段(如外键 id、日期)只写说明不声明类型
|
|
3
|
+
const str = description => ({ type: 'string', nullable: true, description });
|
|
4
|
+
const num = description => ({ type: 'number', nullable: true, description });
|
|
5
|
+
const bool = description => ({ type: 'boolean', nullable: true, description });
|
|
6
|
+
const any = description => ({ description });
|
|
7
|
+
const list = (description, items) => ({ type: 'array', nullable: true, description, items });
|
|
8
|
+
const obj = (description, properties = {}) => ({ type: 'object', nullable: true, description, additionalProperties: true, properties });
|
|
9
|
+
|
|
10
|
+
const success = (description, schema) => ({
|
|
11
|
+
200: Object.assign({}, schema || { type: 'object' }, { description })
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const empty = (description = '操作成功,返回空对象') => success(description);
|
|
15
|
+
|
|
16
|
+
const pageResult = (description, item) => ({
|
|
17
|
+
type: 'object',
|
|
18
|
+
additionalProperties: true,
|
|
19
|
+
properties: {
|
|
20
|
+
pageData: list(`当前页${description}列表`, item),
|
|
21
|
+
totalCount: num('符合条件的总条数')
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const STATUS = '状态:0 开启,1 禁用';
|
|
26
|
+
|
|
27
|
+
const secret = obj('密钥', {
|
|
28
|
+
id: str('密钥 ID(同 appId)'),
|
|
29
|
+
appId: str('AppId,即密钥 ID'),
|
|
30
|
+
secretKey: str('脱敏后的密钥,只保留首尾各 3 位'),
|
|
31
|
+
user: any('所属用户,未关联到用户时为 null'),
|
|
32
|
+
description: str('描述'),
|
|
33
|
+
lastVisitedAt: any('最后一次验签成功的时间'),
|
|
34
|
+
status: num(STATUS),
|
|
35
|
+
createdAt: any('创建时间'),
|
|
36
|
+
updatedAt: any('更新时间')
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
module.exports = { str, num, bool, any, list, obj, success, empty, pageResult, STATUS, secret };
|