Python3实现微信公众平台Token验证与接口请求验签

2024年04月15日 django Python51

这一篇教程,我们一起了解关于微信公众平台开发的基本配置(包括服务器设置和沙箱设置以及Token验证),以及通过获取微信公众平台沙箱密钥了解接口调用时的验签过程。

首先要提醒大家,微信公众平台开放的接口只有一部分,微信支付接口需要先进行公众号认证或者到微信开放平台进行开发者身份验证才能使用,验证费每年300元。

我想这一定是一个假的开放平台…

虽然,我做了各种尝试,看能不能进行微信支付相关的开发测试,结果这个问题的答案是…

一、微信公众平台开发配置

开发配置是进行基于微信公众平台接口开发的基础,必须先完成这项设置。

登录公众平台之后,在页面左下方【开发】分类中点击【基本配置】进入配置页面。

这里能够看到公众号开发信息。

公众号开发信息的下方,我们需要进行服务器配置。

这里的配置项包含:

  • URL:需要填写服务器地址,如果是“http”必须是“80”端口,而“https”必须是“443”端口。所以,我们在自己的电脑上,启动Django的开发服务器时,一定要“python manage.py runserver 0:80”才可以(0是0.0.0.0的简写)。
  • Token:这项随意填写,可以是数字或字母以及数字字母组合,生产环境中使用时最好设置复杂一些。
  • EncodingAESKey:随机生成的开发密钥,用于消息加密。
  • 消息加解密方式(Encrypt Mode):默认明文(Normal)即可。

完成信息的填写之后,点击提交按钮进行Token验证。

此时,微信公众平台服务器会向URL指向的服务器发送GET请求,我们需要编写代码获取请求,并向微信公众平台服务器返回信息。

1、安装微信SDK

pip install wechat-sdk

2、设置URL

示例代码:(urls.py)

path('weixin/', site_view.wechat),

3、编写视图函数

示例代码:(views.py)

from django.http.response import HttpResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt  # 解除csrf验证
from wechat_sdk import WechatConf
from wechat_sdk import WechatBasic

conf = WechatConf(  # 实例化配置信息对象
    token='123456',  # 服务器配置-Token
    appid='wx72fa0b87d2ba4704',  # 公众号开发信息-开发者ID
    appsecret='22843ca13f448b20709bc3bf57a284b2',  # 公众号开发信息-开发者密码
    encrypt_mode='normal',  # 服务器配置-明文模式
    encoding_aes_key='1b9iW6t8XUKEhQkVfgVlPqjawX23qCq9oYYvVLUUDvB'  # 服务器配置-EncodingAESKey
)


@csrf_exempt  # 去除csrf验证
def wechat(request):
    signature = request.GET.get('signature')  # 获取请求信息
    timestamp = request.GET.get('timestamp')
    nonce = request.GET.get('nonce')
    wechat_instance = WechatBasic(conf=conf)  # 实例化微信基类对象
    if not wechat_instance.check_signature(signature=signature, timestamp=timestamp, nonce=nonce):  # 检查验证请求的签名
        return HttpResponseBadRequest('Verify Failed')
    else:
        if request.method == 'GET':
            return HttpResponse(request.GET.get('echostr', None))  # 返回请求中的回复信息

提交并通过Token验证之后,会返回到之前的页面,点击“启用”按钮启用服务器配置。

另外,微信也提供了沙箱环境,沙箱环境也需要进行Token验证,完成以上步骤之后,在页面左侧下方打开开发者工具页面,右侧找到【公众平台测试账号】进入沙箱环境。也可以通过地址进入:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

进入之后,填入“URL”和“Token”(和服务器配置保持一致)进行验证即可。

注意:

  • 要确保服务器能通过外网访问,并能够访问80端口,否则无法完成验证。
  • 服务器使用Windows系统也可能导致“请求URL超时”错误。

如果服务器不能通过外网访问,可以要求宽带服务商将宽带IP转为公网IP,一般都能更改,需要等待1~2天。

如果使用路由器,还需要进行端口转发(端口映射)或者开启DMZ功能,保证外网的访问能够进入局域网中指定主机的指定端口。

如果上述方法不能解决问题,以及Windows系统导致的“请求URL超时”问题,可以通过软件“Ngrok”来解决。

1、打开“Ngrok”官网下载页面:https://ngrok.com/download下载对应自己系统的软件。

2、点击页面右上角的“Sign Up”按钮,注册账号获取授权。注册如果不成功,下方还有通过Github和Google登录,点击之后授权登录即可。

3、登录之后,点击页面左侧的“Auth”菜单项,打开之后复制右侧的“Authtoken”。

4、打开下载好的软件,输入命令:ngrok authtoken [刚才复制的Auth Token],进行激活。

5、激活之后输入命令:ngrok http 80,即可打开外网访问,外网访问地址在软件界面中。

不过Ngrok因为是国外服务器,不是很稳定。

可以考虑使用国内版本,下载地址是https://natapp.cn/

NATAPP提供免费隧道和收费隧道,免费版每次启动后提供的二级域名会有变化,收费版是固定的二级域名,可以绑定自己的域名。

购买收费版本,可以使用优惠码:829AF307。

注册并购买隧道后(免费的也需要进行购买步骤),就可以获取到授权码。

打开软件,执行命令: natapp -authtoken=授权码

就可以使用穿透内网了。

二、微信接口验签

微信公众平台的接口在请求和响应过程中都需要进行签名验证,验证的过程和支付宝类似,这里简单通过获取微信公众平台沙箱密钥做个举例。

参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1

请求Url:https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey

请求方式:POST

请求参数:

  • mch_id:商户号
  • nonce_str:32位以内随机字符串
  • sign:签名

关于签名的算法可以参考官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=4_3

因为最终要向微信公众平台服务器发送的数据是XML格式,所以先定义一个字典转XML的函数。

示例代码:(来自网络)

def trans_dict_to_xml(data):  # 定义字典转XML的函数
    xml = []
    for k in sorted(data.keys()):  # 遍历字典排序后的key
        v = data.get(k)  # 取出字典中key对应的value
        if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML标记
            v = '<![CDATA[{}]]>'.format(v)
        xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
    return '<xml>{}</xml>'.format(''.join(xml))  # 返回XML

接下来,我们完成密钥获取。

示例代码:

def get_sign_key():
    '''
        本函数中的使用的参数和密钥数据均为虚构。
    '''
    url = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey'  # 这里是沙箱接口地址,正式接口去掉“sandboxnew/”。
    params = {'mch_id': '10000100', 'nonce_str': 'ibuaiVcKdpRxkhJA'}  # 请求参数
    params_list = sorted(params.items(), key=lambda e: e[0], reverse=False)  # 参数字典倒排序为列表
    params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=7FCAAD76175001DD40BDB1986600FF7A'
    # 组织参数字符串并在末尾添加商户交易密钥
    md5 = hashlib.md5()  # 使用MD5加密模式
    md5.update(params_str.encode())  # 将参数字符串传入
    sign = md5.hexdigest().upper()  # 完成加密并转为大写
    params.setdefault('sign', sign)  # 参数字典增加签名
    xml = trans_dict_to_xml(params)  # 转换字典为XML
    print(xml)
    response = requests.request('post', url, data=xml)  # 以POST方式向微信公众平台服务器发起请求
    print(response.content.decode())
    return response.content  # 返回响应结果


def trans_dict_to_xml(data):  # 定义字典转XML的函数
    xml = []
    for k in sorted(data.keys()):  # 遍历字典排序后的key
        v = data.get(k)  # 取出字典中key对应的value
        if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML标记
            v = '<![CDATA[{}]]>'.format(v)
        xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
    return '<xml>{}</xml>'.format(''.join(xml))  # 返回XML

运行函数之后,服务器会返回响应的结果,格式同样为XML。

另外,验证签名是否正确的话,也可以使用签名校验工具【点击进入】

当成功获取密钥时,返回结果为:

<xml>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <return_msg><![CDATA[ok]]></return_msg>
  <sandbox_signkey><![CDATA[c4966d17**********50d3069097]]></sandbox_signkey>
</xml>

本文链接:http://so.lmcjl.com/news/2162/

展开阅读全文