【问题标题】:Slack Interactive Messages: POST request payload has an unexpected formatSlack 交互式消息:POST 请求有效负载具有意外的格式
【发布时间】:2019-03-28 08:06:02
【问题描述】:

我在 Slack 的 Flask 应用程序中收到一个 POST 请求。当用户按下交互式消息按钮时发送请求。根据 Slack docs 我必须提取请求的正文以验证签名。 不过,我计算的签名与 Slack 发送的签名不匹配。 事实上,请求的主体是一些编码字符串。正如预期的那样,该字符串实际上是一个编码字典而不是查询 str 参数。

这是我观点的开始:

@app.route('/register', methods=['POST'])
def register_visit():
    data = request.get_data()
    signature = request.headers.get('X-Slack-Signature', None)
    timestamp = request.headers.get('X-Slack-Request-Timestamp', None)
    signing_secret = b'aaaaaaaaaaaaaaaa'


    # old message, ignore
    if round(actual_time.time() - float(timestamp)) > 60 * 5:
        return
    concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8')
    computed_signature = 'v0=' + hmac.new(signing_secret, msg=concatenated, digestmod=hashlib.sha256).hexdigest()
    if hmac.compare_digest(computed_signature, signature):
        ...

我尝试格式化接收到的数据,使其看起来像:

token=fdjkgjl&user_id=1234... 但我不知道数据中必须存在的所有必要参数。

高度赞赏任何想法。

邮件正文如下 - 在 URL 解码后(注意我修改了可能的敏感数据):

b'payload={"type":"interactive_message","actions": [{"name":"yes_button","type":"button","value":"236"}],"callback_id":"visit_button","team":{"id":"fffff"," domain":"ffff"},"channel":{"id":"ffff","name":"directmessage"},"user":{"id":"ffffff","name":"fffft" },"action_ts":"1540403943.419120","message_ts":"1541403949.000100","attachment_id":"1","token":"8LpjBuv13J7xAjhl2lEajoBU","is_app_unfurl":false,"original_message":{"text":"测试","bot_id":"DDDDDDDDDD","attachments":[{"callback_id":"visit_button","text":"Register","id":1,"color":"3AA3E3","actions" :[{"id":"1","name":"yes_button","text":"Yes","type":"button","value":"236","style":""} ],"fallback":"Register"}],"type":"message","subtype":"bot_message","ts":"1540413949.000100"},"response_url":"https://hooks.slack. com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":"464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'

【问题讨论】:

  • 看看官方的python slack API客户端:github.com/slackapi/python-slackclient
  • 在我看来,您混淆了请求的标头和正文。标头将包含签名,正文将包含请求中的所有其他内容(例如用户 ID)。请注意,正文是具有单个属性 payload 的 POST 表单。该属性包含 JSON 数组形式的请求数据。见here
  • @ErikKalkoken 您到底在哪里看到我让他们感到困惑?正文确实包含有效负载。这正是我得到的。但是检查一下,正文是这样的:b'payload=%7B%22type%22%3A%22interactive_message%22%2C%22actions%22%3A%5B%7B%22name%22%3A%22kaynti。 ..'
  • @Fian 好吧,我想要实现的功能不需要任何库。这只是一个常规的 POST 请求,我应该能够使用 Python/Flask 内置功能来处理它。您是否使用该库来处理交互式消息?
  • 也许我当时理解错了。道歉。你的身体是 URL 编码的。我对 Python / Flask 不是很熟悉,但您应该使用 request.form.get('payload') 以纯文本形式获取它。 Source

标签: python flask slack-api


【解决方案1】:

您获得“乱码”数据的原因是您使用的是request.get_data()。该方法将返回请求的原始数据,但不会为您进行任何解码。

更方便的是使用request.form.get('payload'),它会直接给你请求对象的JSON字符串。然后,您可以使用 json.loads() 将其转换为 dict 对象,以便在您的应用程序中进一步处理它。

请注意,您收到的格式是交互式消息的正确格式。您不会得到您建议的查询字符串(例如“token=abc;user_id?def...”)(例如斜杠命令请求)。交互式消息请求将始终将请求作为 JSON 字符串包含在有效负载表单属性中。参考here

这是一个简单的工作示例,它将向按下按钮的用户回复问候。它将直接与 Slack 一起使用,但我建议使用 Postman 对其进行测试。

#app.py

from flask import Flask, request #import main Flask class and request object
import json

app = Flask(__name__) #create the Flask app

@app.route('/register', methods=['POST'])
def register_visit():
    slack_req = json.loads(request.form.get('payload'))
    response = '{"text": "Hi, <@' + slack_req["user"]["id"] + '>"}'
    return response, 200, {'content-type': 'application/json'}

if __name__ == '__main__':
    app.run(debug=True, port=5000) #run app in debug mode on port 5000

【讨论】:

  • 好的,也许我在发布这个问题时并不清楚。我想你误解了我在问什么。这就是原始主体(这是我真正需要的)的样子:token=xyzz0WbapA4vBCDEFasx0q6G&amp;team_id=T1DC2JH3J&amp;team_domain=testteamnow&amp;channel_id=G8PSS9T3V&amp;channel_name=foobar&amp;user_id=U2CERLKJA&amp;user_name=roadrunner&amp;command=%2Fwebhook-collect&amp;text=&amp;response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&amp;trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c。作为查询字符串,而不是字典/JSON。
  • 正如我所说:这是一个查询字符串,就像您从斜杠命令请求中获得的一样。但是您不能从交互式操作请求中获得它。如果您需要该特定格式,则需要手动将其从 JSON 对象转换为平面查询字符串。这就是你要找的吗?
  • 换句话说:你的前提是错误的。您正在寻找从交互式操作请求中获取查询字符串。但这不是 Slack 将发送给您的。 Slack 将在表单属性中向您发送 JSON 字符串。请仔细检查我链接你的文档,你会发现我是正确的。这里是:api.slack.com/docs/…
  • 好的,谢谢!但是,我从乱码消息中计算的签名与 Slack 发送的签名不对应。这就是为什么我认为格式是错误的。顺便说一句,检查这部分:api.slack.com/docs/… 这里提到我应该专门获取原始数据并从中计算签名。
【解决方案2】:

好的,该问题与 Slack 如何向我发送消息无关。这是关于误解哪些数据以字节为单位,哪些数据是 unicode。就我而言,罪魁祸首是字符串格式——concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8') 应该是concatenated = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data))。数据已经是字节,时间戳同时是 unicode。 不敢相信我已经为此努力了好几个小时-_-

@app.route('/register', methods=['POST'])
def register_visit():
    data = request.get_data()
    signature = request.headers.get('X-Slack-Signature', None)
    timestamp = request.headers.get('X-Slack-Request-Timestamp', None)
    signing_secret = b'aaaaaaaaaaaaaaaa'
    # old message, ignore
    if round(actual_time.time() - float(timestamp)) > 60 * 5:
        return
    concatenated = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data))
    computed_signature = 'v0=' + hmac.new(signing_secret, msg=concatenated, 
    digestmod=hashlib.sha256).hexdigest()
    if hmac.compare_digest(computed_signature, signature):
        ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多