鱼C论坛

 找回密码
 立即注册
查看: 893|回复: 43

[已解决]用Python实现Gitee的webhook出问题了

[复制链接]
发表于 2023-10-26 22:12:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 如默 于 2023-10-26 22:15 编辑

如题,我用Python实现gitee的webhook,用的是flask,代码如下:
  1. import os
  2. import time
  3. import hmac
  4. import hashlib
  5. import base64
  6. import urllib
  7. from flask import Flask, request

  8. app = Flask(__name__)

  9. SECRET = 'mySecret'  # 替换为您的签名密钥

  10. def generate_signature(timestamp):
  11.     timestamp_str = str(timestamp)
  12.     secret_enc = SECRET.encode('utf-8')
  13.     string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
  14.     string_to_sign_enc = string_to_sign.encode('utf-8')
  15.     hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
  16.     signature = urllib.parse.quote_plus(base64.b64encode(hmac_code))
  17.     # signature = base64.b64encode(hmac_code).decode()
  18.     # url_encoded_signature = urllib.parse.quote(signature, safe='')
  19.     return signature

  20. @app.route('/webhook', methods=['POST'])
  21. def handle_webhook():
  22.     # 验证请求来源是否是Gitee
  23.     if request.headers.get('User-Agent') == 'git-oschina-hook':
  24.         # 验证签名
  25.         timestamp = int(request.headers.get('X-Gitee-Timestamp'))
  26.         actual_signature = request.headers.get('X-Gitee-Token')
  27.         expected_signature = generate_signature(timestamp)
  28.         if actual_signature != expected_signature:
  29.             return '', 403  # 返回拒绝访问状态码

  30.         # 在这里编写处理Webhook请求的代码逻辑
  31.         data = request.json
  32.         print('Received webhook request:', data)
  33.         # 执行 git pull、pnpm install 和 pnpm build 等操作
  34.         # ...

  35.         return '', 200  # 返回成功状态码
  36.     else:
  37.         return '', 403  # 返回拒绝访问状态码

  38. if __name__ == '__main__':
  39.     app.run(host='0.0.0.0', port=3002)
复制代码


放在服务器上运行之后,在gitee的后台测试,发现报403错误,gitee的返回的token信息如下:uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0=

下面是完整的返回的header信息

  1. Request URL: https://xxx.com/webhook
  2. Request Method: POST
  3. X-Gitee-Token: uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0=
  4. X-Gitee-Event: push_hooks
  5. User-Agent: git-oschina-hook
  6. X-Gitee-Timestamp: 1698328784458
  7. X-Gitee-Ping: true
  8. Content-Type: application/json
  9. X-Git-Oschina-Event: push_hooks
复制代码


查看flask的运行日志,得到的日志内容是:

  1. 127.0.0.1 - - [26/Oct/2023 21:59:44] "POST /webhook?sign=uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0%3D&timestamp=1698328784458 HTTP/1.1" 403 -
复制代码


可以看到token是不一致的,差了一个=号,不知道为什么,官方文档的地址是:https://help.gitee.com/webhook/how-to-verify-webhook-keys

哪位大佬能帮忙看看啊,实在不知道啥问题了,唉
最佳答案
2023-10-26 23:49:38
如默 发表于 2023-10-26 22:22
唉,还是不行啊,这是代码:
  1. import os
  2. import time
  3. import hmac
  4. import hashlib
  5. import base64
  6. import urllib
  7. from flask import Flask, request

  8. app = Flask(__name__)

  9. SECRET = 'mySecret'  # 替换为您的签名密钥

  10. def generate_signature(timestamp):
  11.     timestamp_str = str(timestamp)
  12.     secret_enc = SECRET.encode('utf-8')
  13.     string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
  14.     string_to_sign_enc = string_to_sign.encode('utf-8')
  15.     hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
  16.     signature = base64.b64encode(hmac_code).decode()
  17.     return signature

  18. @app.route('/webhook', methods=['POST'])
  19. def handle_webhook():
  20.     # 验证请求来源是否是Gitee
  21.     if request.headers.get('User-Agent') == 'git-oschina-hook':
  22.         # 验证签名
  23.         timestamp = int(request.headers.get('X-Gitee-Timestamp'))
  24.         actual_signature = request.headers.get('X-Gitee-Token')
  25.         expected_signature = generate_signature(timestamp)
  26.         if actual_signature != expected_signature:
  27.             return '', 403  # 返回拒绝访问状态码

  28.         # 在这里编写处理Webhook请求的代码逻辑
  29.         data = request.json
  30.         print('Received webhook request:', data)
  31.         # 执行 git pull、pnpm install 和 pnpm build 等操作
  32.         # ...

  33.         return '', 200  # 返回成功状态码
  34.     else:
  35.         return '', 403  # 返回拒绝访问状态码

  36. if __name__ == '__main__':
  37.     app.run(host='0.0.0.0', port=3002)
复制代码

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-10-26 22:13:00 | 显示全部楼层
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名进行url编码,而实际上应该使用 urllib.parse.quote 方法。



正确的代码如下:



  1. import os

  2. import time

  3. import hmac

  4. import hashlib

  5. import base64

  6. import urllib

  7. from flask import Flask, request



  8. app = Flask(__name__)



  9. SECRET = 'mySecret'  # 替换为您的签名密钥



  10. def generate_signature(timestamp):

  11.     timestamp_str = str(timestamp)

  12.     secret_enc = SECRET.encode('utf-8')

  13.     string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)

  14.     string_to_sign_enc = string_to_sign.encode('utf-8')

  15.     hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()

  16.     signature = urllib.parse.quote(base64.b64encode(hmac_code), safe='')

  17.     return signature



  18. @app.route('/webhook', methods=['POST'])

  19. def handle_webhook():

  20.     # 验证请求来源是否是Gitee

  21.     if request.headers.get('User-Agent') == 'git-oschina-hook':

  22.         # 验证签名

  23.         timestamp = int(request.headers.get('X-Gitee-Timestamp'))

  24.         actual_signature = request.headers.get('X-Gitee-Token')

  25.         expected_signature = generate_signature(timestamp)

  26.         if actual_signature != expected_signature:

  27.             return '', 403  # 返回拒绝访问状态码



  28.         # 在这里编写处理Webhook请求的代码逻辑

  29.         data = request.json

  30.         print('Received webhook request:', data)

  31.         # 执行 git pull、pnpm install 和 pnpm build 等操作

  32.         # ...



  33.         return '', 200  # 返回成功状态码

  34.     else:

  35.         return '', 403  # 返回拒绝访问状态码



  36. if __name__ == '__main__':

  37.     app.run(host='0.0.0.0', port=3002)

复制代码



通过使用 urllib.parse.quote 方法对签名进行url编码,并指定 safe 参数为空,可以正确地生成签名字符串。这样,Gitee的返回的token信息与生成的签名就能匹配上了,从而解决了403错误的问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-10-26 22:13:43 | 显示全部楼层

回帖奖励 +50 鱼币

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-10-26 22:17:09 | 显示全部楼层
isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...

我试试
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-10-26 22:22:34 | 显示全部楼层
isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...

唉,还是不行啊,这是代码:
  1. import os
  2. import time
  3. import hmac
  4. import hashlib
  5. import base64
  6. import urllib
  7. from flask import Flask, request

  8. app = Flask(__name__)

  9. SECRET = 'mySecret'  # 替换为您的签名密钥

  10. def generate_signature(timestamp):
  11.     timestamp_str = str(timestamp)
  12.     secret_enc = SECRET.encode('utf-8')
  13.     string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
  14.     string_to_sign_enc = string_to_sign.encode('utf-8')
  15.     hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
  16.     signature = urllib.parse.quote(base64.b64encode(hmac_code), safe='')
  17.     # signature = base64.b64encode(hmac_code).decode()
  18.     # url_encoded_signature = urllib.parse.quote(signature, safe='')
  19.     return signature

  20. @app.route('/webhook', methods=['POST'])
  21. def handle_webhook():
  22.     # 验证请求来源是否是Gitee
  23.     if request.headers.get('User-Agent') == 'git-oschina-hook':
  24.         # 验证签名
  25.         timestamp = int(request.headers.get('X-Gitee-Timestamp'))
  26.         actual_signature = request.headers.get('X-Gitee-Token')
  27.         expected_signature = generate_signature(timestamp)
  28.         if actual_signature != expected_signature:
  29.             return '', 403  # 返回拒绝访问状态码

  30.         # 在这里编写处理Webhook请求的代码逻辑
  31.         data = request.json
  32.         print('Received webhook request:', data)
  33.         # 执行 git pull、pnpm install 和 pnpm build 等操作
  34.         # ...

  35.         return '', 200  # 返回成功状态码
  36.     else:
  37.         return '', 403  # 返回拒绝访问状态码

  38. if __name__ == '__main__':
  39.     app.run(host='0.0.0.0', port=3002)
复制代码


这是flask日志:
  1. 127.0.0.1 - - [26/Oct/2023 22:20:08] "POST /webhook?sign=%200CTC8AsNbKn%20SZde1kyMYsnIP0b6nJ8IWoO7pqn754%3D&timestamp=1698330008081 HTTP/1.1" 403 -
复制代码


这是Gitee后台header信息:
  1. Request URL: https://xxx.com/webhook
  2. Request Method: POST
  3. X-Gitee-Token: +0CTC8AsNbKn+SZde1kyMYsnIP0b6nJ8IWoO7pqn754=
  4. X-Gitee-Event: push_hooks
  5. User-Agent: git-oschina-hook
  6. X-Gitee-Timestamp: 1698330008081
  7. X-Gitee-Ping: true
  8. Content-Type: application/json
  9. X-Git-Oschina-Event: push_hooks
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-10-26 22:23:24 | 显示全部楼层
isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...

才发现,你说的这个我之前就试过了,代码里面注释了,唉,难顶
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-10-26 23:49:38 | 显示全部楼层    本楼为最佳答案   
如默 发表于 2023-10-26 22:22
唉,还是不行啊,这是代码:
  1. import os
  2. import time
  3. import hmac
  4. import hashlib
  5. import base64
  6. import urllib
  7. from flask import Flask, request

  8. app = Flask(__name__)

  9. SECRET = 'mySecret'  # 替换为您的签名密钥

  10. def generate_signature(timestamp):
  11.     timestamp_str = str(timestamp)
  12.     secret_enc = SECRET.encode('utf-8')
  13.     string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
  14.     string_to_sign_enc = string_to_sign.encode('utf-8')
  15.     hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
  16.     signature = base64.b64encode(hmac_code).decode()
  17.     return signature

  18. @app.route('/webhook', methods=['POST'])
  19. def handle_webhook():
  20.     # 验证请求来源是否是Gitee
  21.     if request.headers.get('User-Agent') == 'git-oschina-hook':
  22.         # 验证签名
  23.         timestamp = int(request.headers.get('X-Gitee-Timestamp'))
  24.         actual_signature = request.headers.get('X-Gitee-Token')
  25.         expected_signature = generate_signature(timestamp)
  26.         if actual_signature != expected_signature:
  27.             return '', 403  # 返回拒绝访问状态码

  28.         # 在这里编写处理Webhook请求的代码逻辑
  29.         data = request.json
  30.         print('Received webhook request:', data)
  31.         # 执行 git pull、pnpm install 和 pnpm build 等操作
  32.         # ...

  33.         return '', 200  # 返回成功状态码
  34.     else:
  35.         return '', 403  # 返回拒绝访问状态码

  36. if __name__ == '__main__':
  37.     app.run(host='0.0.0.0', port=3002)
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-10-27 09:02:40 | 显示全部楼层

感谢,这个代码跑成功了,还是想请教一下,我看后台的日志,输出的内容还是和之前一样的,但这次就返回200,想请教一下为什么
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-10-27 09:10:56 From FishC Mobile | 显示全部楼层
如默 发表于 2023-10-27 09:02
感谢,这个代码跑成功了,还是想请教一下,我看后台的日志,输出的内容还是和之前一样的,但这次就返回20 ...

在请求头的内容没有url编码,所以我把url编码去掉了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-10-27 10:52:53 | 显示全部楼层
isdkz 发表于 2023-10-27 09:10
在请求头的内容没有url编码,所以我把url编码去掉了

那这官方文档误人子弟啊,文档上写的要urlencode,结果他自己的请求头没有urlencode,导致我匹配不上。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-10-27 10:56:48 | 显示全部楼层
如默 发表于 2023-10-27 10:52
那这官方文档误人子弟啊,文档上写的要urlencode,结果他自己的请求头没有urlencode,导致我匹配不上。

看你是从哪里取他传的签名密钥了,如果你是从 url 参数那里取的就要 url 编码,如果你是从请求头那里取出来的就不用
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-10-27 11:04:01 | 显示全部楼层
本帖最后由 如默 于 2023-10-27 11:07 编辑
isdkz 发表于 2023-10-27 10:56
看你是从哪里取他传的签名密钥了,如果你是从 url 参数那里取的就要 url 编码,如果你是从请求头那里取出 ...


gitee后台的日志啊, clipbord_1698375806038.png


她后台返回的这个数据,显然是请求头的,而我给她发的数据是拼在url里的,所以对不上,因为她的这个没有url编码,很无语。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-10-27 11:09:23 | 显示全部楼层
如默 发表于 2023-10-27 11:04
gitee后台的日志啊,


他在请求头和url参数都传了签名的,flask的运行日志中 sign 参数就是他在 url 中传的签名
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-10-27 11:26:08 | 显示全部楼层
isdkz 发表于 2023-10-27 11:09
他在请求头和url参数都传了签名的,flask的运行日志中 sign 参数就是他在 url 中传的签名

那之前为什么不对呢,一直返回403,这不是代码校验的时候错误吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-12-12 16:45:28 | 显示全部楼层
剩下的回帖奖励呢,怎么没人要了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-12-13 09:57:19 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-12-13 09:58:09 | 显示全部楼层
哇喔,
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-12-13 09:58:17 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-1-9 09:27:00 | 显示全部楼层
育碧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-1-9 09:27:30 | 显示全部楼层

回帖奖励 +50 鱼币

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-23 15:01

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表