正如Slack的官方文件所说
有一些博客和教程可帮助您进行此验证,但是几乎所有的博客都忽略了过程中一个非常重要的一点。因此,当我不得不在后端服务器API服务中实现此验证过程时,我挣扎了很长时间,因为来自Slack的签名和服务器中我计算的签名不匹配。我终于在Slack的团队的一项建议下工作了。
我们需要确认您正在提取回调请求有效载荷的原始版本,并在代码中使用时以其“原始”形式保存?这很重要,因为Slack的系统使用了请求主体的“原始”形式,包括Whitespace,用于计算签名。
有关更多详细信息,您可以检查以这样的方式编写这个非常重要的观点的正式文档,如果您对每一行不太注意,您一定会想念它,就像我自己一样。
正式所有步骤都已在此处的官方文档中可用
本文的主要目的是向您展示如何获得请求主体是原始格式,然后使用它来计算签名,以将其与Slack侧面提供的签名匹配。 p>
步骤像这样:
1.更新您的主JavaScript文件
您初始化了Express应用的文件,即服务器API服务的入口点
const express = require('express');
const app = express();
...
...
...
app.listen(8000, () => console.log(`server started`));
在app
中添加以下中间Wares,以在const app = express();
之后获取原始请求主体:
- 对于
application/json
内容类型
app.use(
express.json({
verify: (req, _, buf) => {
req.rawBody = buf;
},
})
);
- 对于
application/x-www-form-urlencoded
内容类型
app.use(
express.urlencoded({
extended: true,
verify: (req, _, buf) => {
req.rawBody = buf;
},
})
);
现在,您可以在应用程序中的任何API路由或任何中间件内访问原始请求主体,如下:
const router = require('express').Router();
...
...
...
router.post('/some-route', (req, res) => {
console.log({rawBody: req.rawBody});
...
...
...
});
2.验证来自Slack的请求
这是从Slack验证签名请求的过程的概述:
- 在HTTP请求和请求的正文上检索
X-Slack-Request-Timestamp
标头。 - 串联
version number
,timestamp
和body
,要求形成basestring
。使用结肠作为三个元素之间的定界符。例如,v0:123456789:command=/weather&text=94070
。现在的版本编号始终是v0
。 - 在NPM package crypto在NPM package crypto中实现的HMAC SHA256的帮助下,将上述
basestring
hash用作密钥。 在请求中比较此计算的签名与X-Slack-Signature
标头。
此软件包加密现在是一个内置节点模块。如果您依靠加密货币,则应切换到内置的。
在这里,我编写了一种中间件方法,该方法在API路由中使用用于处理Slack请求的[消息操作,数据提交,斜杠命令,Webhook有效载荷等]
。中间件的代码如下。该方法写在名为webhookVerifier.js
的文件中,后来该方法在所需的文件中导入。
const crypto = require('crypto');
...
...
...
const slack = (req, res, next) => {
// verify that the timestamp does not differ from local time by more than five minutes
if (
!req.headers['x-slack-request-timestamp'] ||
Math.abs(
Math.floor(new Date().getTime() / 1000) -
+req.headers['x-slack-request-timestamp']
) > 300
)
return res.status(400).send('Request too old!');
// compute the basestring
const baseStr = `v0:${req.headers['x-slack-request-timestamp']}:${req.rawBody}`;
// extract the received signature from the request headers
const receivedSignature = req.headers['x-slack-signature'];
// compute the signature using the basestring
// and hashing it using the signing secret
// which can be stored as a environment variable
const expectedSignature = `v0=${crypto
.createHmac('sha256', process.env.SLACK_SIGNING_SECRET)
.update(baseStr, 'utf8')
.digest('hex')}`;
// match the two signatures
if (expectedSignature !== receivedSignature) {
console.log('WEBHOOK SIGNATURE MISMATCH');
return res.status(400).send('Error: Signature mismatch security error');
}
// signatures matched, so continue the next step
console.log('WEBHOOK VERIFIED');
next();
};
// exporting the method
module.exports = { slack };
3.在路线中添加中间件
您可以在路由文件中导入中间件,并在API路由中使用它作为Slack,如以下
const router = require('express').Router();
// importing the webhook verifier method for slack
const webhookVerifier = require('./path/to/the/webhookVerifier/file/webhookVerifier');
// for managing slack user interactions
router.post('/interact', [webhookVerifier.slack], async (req, res) => {
...
...
...
});
// for handling slack slash commands
router.post('/slash', [webhookVerifier.slack], async (req, res) => {
...
...
...
});
// for managing the slack webhook evevt subscription payloads
router.post('/webhook', [webhookVerifier.slack], async (req, res) => {
...
...
...
});
module.exports = router;
注意:您的API路线与我的路线不同。以上代码只是一个示例
最重要的一点是使用原始请求主体而不是编码的请求主体。因为我的代码中缺少这个小但非常重要的一点,所以我很长一段时间以来一直打断头。希望您的头在本教程后不会得到治疗。
我最近的博客文章ð:
- Integrate Razorpay Payment Gateway in your React app
- Some Lesser Known But Awesome Online Tools For Web Development
- Node & Express server, but with TypeScript !
在网上找到我:
- ð» Visit my Website
- ð〜检查我的Repos on GitHub11
- d检查我的Repos on GitLab
- ð¦ Check my Packages on NPM
- ð查看我的Profile on LinkedIn
- ð查看我的Blogs on Dev.to
- ð在Instagram上关注我
- ð查看我的Goodreads Profile
- ðª与我联系Here