用Python实现Gitee的webhook出问题了
本帖最后由 如默 于 2023-10-26 22:15 编辑如题,我用Python实现gitee的webhook,用的是flask,代码如下:
import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request
app = Flask(__name__)
SECRET = 'mySecret'# 替换为您的签名密钥
def generate_signature(timestamp):
timestamp_str = str(timestamp)
secret_enc = SECRET.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
signature = urllib.parse.quote_plus(base64.b64encode(hmac_code))
# signature = base64.b64encode(hmac_code).decode()
# url_encoded_signature = urllib.parse.quote(signature, safe='')
return signature
@app.route('/webhook', methods=['POST'])
def handle_webhook():
# 验证请求来源是否是Gitee
if request.headers.get('User-Agent') == 'git-oschina-hook':
# 验证签名
timestamp = int(request.headers.get('X-Gitee-Timestamp'))
actual_signature = request.headers.get('X-Gitee-Token')
expected_signature = generate_signature(timestamp)
if actual_signature != expected_signature:
return '', 403# 返回拒绝访问状态码
# 在这里编写处理Webhook请求的代码逻辑
data = request.json
print('Received webhook request:', data)
# 执行 git pull、pnpm install 和 pnpm build 等操作
# ...
return '', 200# 返回成功状态码
else:
return '', 403# 返回拒绝访问状态码
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3002)
放在服务器上运行之后,在gitee的后台测试,发现报403错误,gitee的返回的token信息如下:uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0=
下面是完整的返回的header信息
Request URL: https://xxx.com/webhook
Request Method: POST
X-Gitee-Token: uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0=
X-Gitee-Event: push_hooks
User-Agent: git-oschina-hook
X-Gitee-Timestamp: 1698328784458
X-Gitee-Ping: true
Content-Type: application/json
X-Git-Oschina-Event: push_hooks
查看flask的运行日志,得到的日志内容是:
127.0.0.1 - - "POST /webhook?sign=uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0%3D×tamp=1698328784458 HTTP/1.1" 403 -
可以看到token是不一致的,差了一个=号,不知道为什么,官方文档的地址是:https://help.gitee.com/webhook/how-to-verify-webhook-keys
哪位大佬能帮忙看看啊,实在不知道啥问题了,唉
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名进行url编码,而实际上应该使用 urllib.parse.quote 方法。
正确的代码如下:
import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request
app = Flask(__name__)
SECRET = 'mySecret'# 替换为您的签名密钥
def generate_signature(timestamp):
timestamp_str = str(timestamp)
secret_enc = SECRET.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
signature = urllib.parse.quote(base64.b64encode(hmac_code), safe='')
return signature
@app.route('/webhook', methods=['POST'])
def handle_webhook():
# 验证请求来源是否是Gitee
if request.headers.get('User-Agent') == 'git-oschina-hook':
# 验证签名
timestamp = int(request.headers.get('X-Gitee-Timestamp'))
actual_signature = request.headers.get('X-Gitee-Token')
expected_signature = generate_signature(timestamp)
if actual_signature != expected_signature:
return '', 403# 返回拒绝访问状态码
# 在这里编写处理Webhook请求的代码逻辑
data = request.json
print('Received webhook request:', data)
# 执行 git pull、pnpm install 和 pnpm build 等操作
# ...
return '', 200# 返回成功状态码
else:
return '', 403# 返回拒绝访问状态码
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3002)
通过使用 urllib.parse.quote 方法对签名进行url编码,并指定 safe 参数为空,可以正确地生成签名字符串。这样,Gitee的返回的token信息与生成的签名就能匹配上了,从而解决了403错误的问题。 币 isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...
我试试 isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...
唉,还是不行啊,这是代码:
import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request
app = Flask(__name__)
SECRET = 'mySecret'# 替换为您的签名密钥
def generate_signature(timestamp):
timestamp_str = str(timestamp)
secret_enc = SECRET.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
signature = urllib.parse.quote(base64.b64encode(hmac_code), safe='')
# signature = base64.b64encode(hmac_code).decode()
# url_encoded_signature = urllib.parse.quote(signature, safe='')
return signature
@app.route('/webhook', methods=['POST'])
def handle_webhook():
# 验证请求来源是否是Gitee
if request.headers.get('User-Agent') == 'git-oschina-hook':
# 验证签名
timestamp = int(request.headers.get('X-Gitee-Timestamp'))
actual_signature = request.headers.get('X-Gitee-Token')
expected_signature = generate_signature(timestamp)
if actual_signature != expected_signature:
return '', 403# 返回拒绝访问状态码
# 在这里编写处理Webhook请求的代码逻辑
data = request.json
print('Received webhook request:', data)
# 执行 git pull、pnpm install 和 pnpm build 等操作
# ...
return '', 200# 返回成功状态码
else:
return '', 403# 返回拒绝访问状态码
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3002)
这是flask日志:
127.0.0.1 - - "POST /webhook?sign=%200CTC8AsNbKn%20SZde1kyMYsnIP0b6nJ8IWoO7pqn754%3D×tamp=1698330008081 HTTP/1.1" 403 -
这是Gitee后台header信息:
Request URL: https://xxx.com/webhook
Request Method: POST
X-Gitee-Token: +0CTC8AsNbKn+SZde1kyMYsnIP0b6nJ8IWoO7pqn754=
X-Gitee-Event: push_hooks
User-Agent: git-oschina-hook
X-Gitee-Timestamp: 1698330008081
X-Gitee-Ping: true
Content-Type: application/json
X-Git-Oschina-Event: push_hooks isdkz 发表于 2023-10-26 22:13
问题出在对生成的签名进行url编码时,使用了错误的方法。在代码中,使用了 urllib.parse.quote_plus 对签名 ...
才发现,你说的这个我之前就试过了,代码里面注释了,唉,难顶 如默 发表于 2023-10-26 22:22
唉,还是不行啊,这是代码:
import os
import time
import hmac
import hashlib
import base64
import urllib
from flask import Flask, request
app = Flask(__name__)
SECRET = 'mySecret'# 替换为您的签名密钥
def generate_signature(timestamp):
timestamp_str = str(timestamp)
secret_enc = SECRET.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp_str, SECRET)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(hmac_code).decode()
return signature
@app.route('/webhook', methods=['POST'])
def handle_webhook():
# 验证请求来源是否是Gitee
if request.headers.get('User-Agent') == 'git-oschina-hook':
# 验证签名
timestamp = int(request.headers.get('X-Gitee-Timestamp'))
actual_signature = request.headers.get('X-Gitee-Token')
expected_signature = generate_signature(timestamp)
if actual_signature != expected_signature:
return '', 403# 返回拒绝访问状态码
# 在这里编写处理Webhook请求的代码逻辑
data = request.json
print('Received webhook request:', data)
# 执行 git pull、pnpm install 和 pnpm build 等操作
# ...
return '', 200# 返回成功状态码
else:
return '', 403# 返回拒绝访问状态码
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3002)
isdkz 发表于 2023-10-26 23:49
感谢,这个代码跑成功了,还是想请教一下,我看后台的日志,输出的内容还是和之前一样的,但这次就返回200,想请教一下为什么 如默 发表于 2023-10-27 09:02
感谢,这个代码跑成功了,还是想请教一下,我看后台的日志,输出的内容还是和之前一样的,但这次就返回20 ...
在请求头的内容没有url编码,所以我把url编码去掉了 isdkz 发表于 2023-10-27 09:10
在请求头的内容没有url编码,所以我把url编码去掉了
那这官方文档误人子弟啊,文档上写的要urlencode,结果他自己的请求头没有urlencode,导致我匹配不上。 如默 发表于 2023-10-27 10:52
那这官方文档误人子弟啊,文档上写的要urlencode,结果他自己的请求头没有urlencode,导致我匹配不上。
看你是从哪里取他传的签名密钥了,如果你是从 url 参数那里取的就要 url 编码,如果你是从请求头那里取出来的就不用 本帖最后由 如默 于 2023-10-27 11:07 编辑
isdkz 发表于 2023-10-27 10:56
看你是从哪里取他传的签名密钥了,如果你是从 url 参数那里取的就要 url 编码,如果你是从请求头那里取出 ...
gitee后台的日志啊,
她后台返回的这个数据,显然是请求头的,而我给她发的数据是拼在url里的,所以对不上,因为她的这个没有url编码,很无语。 如默 发表于 2023-10-27 11:04
gitee后台的日志啊,
他在请求头和url参数都传了签名的,flask的运行日志中 sign 参数就是他在 url 中传的签名 isdkz 发表于 2023-10-27 11:09
他在请求头和url参数都传了签名的,flask的运行日志中 sign 参数就是他在 url 中传的签名
那之前为什么不对呢,一直返回403,这不是代码校验的时候错误吗?{:10_266:} 剩下的回帖奖励呢,怎么没人要了 {:10_275:}{:10_275:}{:10_275:} 哇喔,{:5_106:} {:10_257:} 育碧{:10_254:} {:10_298:}{:10_298:}{:10_298:}