鱼C论坛

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

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

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

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

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

x
本帖最后由 如默 于 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 - - [26/Oct/2023 21:59:44] "POST /webhook?sign=uinoZjLDU4UB97wNM1xNB1XCAuTA5Urk8BKYrSxc1g0%3D×tamp=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
唉,还是不行啊,这是代码:
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)

本帖被以下淘专辑推荐:

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

使用道具 举报

发表于 2023-10-26 22:13:00 | 显示全部楼层
问题出在对生成的签名进行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错误的问题。
想知道小甲鱼最近在做啥?请访问 -> 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 对签名 ...

唉,还是不行啊,这是代码:
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 - - [26/Oct/2023 22:20:08] "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
想知道小甲鱼最近在做啥?请访问 -> 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
唉,还是不行啊,这是代码:
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)
想知道小甲鱼最近在做啥?请访问 -> 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, 2025-1-4 02:23

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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