最近闲的蛋疼想抛弃微信官方提供的服务,想把公众号服务都迁移到云服务器上,却发现接口权限太少了…就把瞎捣鼓过程记录一下。
主要是添加url
和token
,这里url
必须以http://
或https://
开头,分别支持80端口和443端口。所以服务器那边就用Nginx监听443端口然后转发到给微信api分配的端口(本文是8000)。
自己的服务器接收到GET
请求时,需要校验消息是不是微信的服务器发过来的,代码如下。
@app.route('/', methods=['GET', 'POST'])
def wechat():
args = request.args
signature = args['signature']
timestamp = args['timestamp']
nonce = args['nonce']
token = 'xxxxxxxxx'
if not all([signature, timestamp, nonce, echostr]):
abort(400)
# 根据token, timestamp, nonce构造hashcode
list = sorted([token, timestamp, nonce])
s = list[0]+list[1]+list[2]
hashcode = hashlib.sha1(s.encode('utf-8')).hexdigest()
if request.method=='GET':
# GET仅用于配置服务器时校验 检查hashcode是否等于signature
echostr = args['echostr']
if hashcode == signature:
return echostr
else:
return "Sign error."
用户发给微信服务器,微信服务器给搞成XML发给自己的服务器,类似下面这样。其中MsgType
是消息类型,比如文本、图片、小程序、视频、语音等。分别根据MsgType
来处理和回复。
<xml>
<ToUserName>ToUserName>
<FromUserName>FromUserName>
<CreateTime>1460537339CreateTime>
<MsgType>MsgType>
<Content>Content>
<MsgId>3272960105994287638MsgId>
xml>
接收到xml后,我用xmltodict.parse
方法将xml转成类似字典进行后续处理,最后用xmltodict.unparse
方法将dict再转成xml返回给微信服务器,微信服务器再发给用户。
完整代码如下:
from flask import Flask, jsonify, request, Response, abort
from gevent import pywsgi
from flask_cors import CORS
import time
import hashlib
import xmltodict
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False # 支持中文
app.config['JSON_SORT_KEYS'] = False # 禁止json自动排序
CORS(app, supports_credentials=True) # 允许跨域
@app.route('/', methods=['GET', 'POST']) #, strict_slashes=False)
def wechat():
args = request.args
signature = args['signature']
timestamp = args['timestamp']
nonce = args['nonce']
token = 'xxxxxxxxxxxx'
if not all([signature, timestamp, nonce]):
abort(400)
list = sorted([token, timestamp, nonce])
s = list[0]+list[1]+list[2]
hashcode = hashlib.sha1(s.encode('utf-8')).hexdigest()
if request.method=='GET':
# 仅用于配置服务器时校验
echostr = args['echostr']
if hashcode == signature:
return echostr
else:
return "Sign error."
if request.method=='POST':
# 监测是否是微信发来的请求
if hashcode != signature:
abort(403)
xml = request.data
if not xml:
abort(400)
# parse:xml -> dict
req = xmltodict.parse(xml)['xml']
print(req)
# 用户发来的是文字
if 'text' == req.get('MsgType'):
resp = {
'ToUserName':req.get('FromUserName'),
'FromUserName':req.get('ToUserName'),
'CreateTime':int(time.time()),
'MsgType':'text',
'Content':req.get('Content')
}
# 用户发来的是语音
elif 'voice' == req.get('MsgType'):
reg = req.get('Recognition')
resp = {
'ToUserName':req.get('FromUserName'),
'FromUserName':req.get('ToUserName'),
'CreateTime':int(time.time()),
'MsgType':'text',
'Content':reg if reg else '你说的嘛呀!听不懂'
}
# 用户发来的是图片
elif 'image' == req.get('MsgType'):
resp = {
'ToUserName': req.get('FromUserName'),
'FromUserName': req.get('ToUserName'),
'CreateTime': int(time.time()),
'MsgType': 'text',
'Content': '好图,好图!'
}
# 用户发来的是位置
elif 'location' == req.get('MsgType'):
resp = {
'ToUserName': req.get('FromUserName'),
'FromUserName': req.get('ToUserName'),
'CreateTime': int(time.time()),
'MsgType': 'text',
'Content': f'原来你在位于(X, Y)=({req.get("Location_X")}, {req.get("Location_Y")})的{req.get("Label")}!'
}
# 用户发来的是视频
elif 'video' == req.get('MsgType'):
resp = {
'ToUserName': req.get('FromUserName'),
'FromUserName': req.get('ToUserName'),
'CreateTime': int(time.time()),
'MsgType': 'text',
'Content': '不要发些奇奇怪怪的东西嗷'
}
# 用户发来的是event(关注/取关)
elif 'event' == req.get('MsgType'):
if "subscribe" == req.get("Event"):
resp = {
"ToUserName":req.get("FromUserName", ""),
"FromUserName":req.get("ToUserName", ""),
"CreateTime":int(time.time()),
"MsgType":"text",
"Content":u"感谢您的关注!"
}
else:
print('取关!')
resp = None
# 用户发来的是其他东西 随便返回个字符串 要不然用户那边显示服务出问题了
else:
return "success"
# unparse:dict -> xml
xml = xmltodict.unparse({
'xml':resp if resp else ''})
return xml
if __name__ == '__main__':
server = pywsgi.WSGIServer(('0.0.0.0', 8000), app)
server.serve_forever()
1、公众号权限设置那里,设置网页授权回调的允许域名
2、用户同意授权,获取code(用户访问如下网址,REDIRECT_URI就是点完授权要跳转到的URL,跳转时会带着code
参数)
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
3、通过第2步拿到的code
换取网页授权access_token
。方法是在自己服务端发GET请求如下:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
4、拿着ACCESS_TOKEN
和OPENID
换用户基本信息,也是GET请求:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN