一、微信公众号开发的意义
目前微信公众平台提供给公众号的功能只是简单的被动回复,主动发送,内嵌网页等,无法满足许多用户的需求,所以就需要根据微信平台提供的接口进行进一步开发。
二、微信公众号开发模型:
三、接入微信公众平台
3.1填写服务器配置:
登录微信公众平台官网后,在公众平台后台管理页面 - 开发者中心页,点击“修改配置”按钮
3.2分析:
URL是开发者用来接收微信消息和事件的接口URL。
Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。
EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。
加解密方式的默认状态为明文模式,选择兼容模式和安全模式需要提前配置好相关加解密代码
3.3利用测试平台
测试平台登陆地址http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
3.4验证服务器地址的有效性:
3.4.1开发者提交信息后,微信服务器将发送GET请求携带4个参数到填写的服务器地址上:
校验流程:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
代码实现:
@app.route('/wechat8000')
def wechat():
#设置token
token = 'python'
#获取参数
data = request.args
signature = data.get('signature')
timestamp = data.get('timestamp')
nonce = data.get('nonce')
echostr = data.get('echostr')
#对参数进行字典排序,拼接字符串
temp = [timestamp,nonce,token]
temp.sort()
temp = ''.join(temp)
#使用sha1加密,与signature进行比较
if (hashlib.sha1(temp).hexdigest()==signature):
return make_response(echostr)
else:
return 'error',403
if __name__ == '__main__':
app.run(port=8000)
3.5开发者服务器配置:
3.5.1首先要有一台公网服务器(阿里云)
3.5.2连接服务器: ssh s80xx@101.200.170.171 #服务器账号@服务器IP地址
3.5.3切换到flask_py2 虚拟环境:workon flask_py2
如果提示workon命令未找到:source ~/.bashrc
3.5.4拷贝文件到服务器:
#scp 源文件 目标文件
scp ./wechat.py s80xx@101.200.170.171:~/xxxx/
3.5.5在微信开发平台输入服务器URL和Token进行验证
四、公众号接收与发送消息
4.1概念:
4.1.1验证URL有效性成功后即接入生效,成为开发者。如果公众号类型为服务号(订阅号只能使用普通消息接口),可以在公众平台网站中申请认证,认证成功的服务号将获得众多接口权限,以满足开发者需求。
4.1.2此后用户每次向公众号发送消息、或者产生自定义菜单点击事件时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,然后开发者可以依据自身业务逻辑进行响应,例如回复消息等。
4.1.3用户向公众号发送消息时,公众号方收到的消息发送者是一个OpenID,是使用用户微信号加密后的结果,每个用户对每个公众号有一个唯一的OpenID。
4.2接收普通消息:
4.2.1当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
4.2.2XML数据包格式:
<xml>
#开发者公众号
#用户账号(OpenID)
1348831860#消息创建时间戳
#消息类型,text是文本类型消息
#文本消息内容
1234567890123456 #消息的ID
</xml>
4.3回复普通消息:
4.3.1当用户发送消息给公众号时,会产生一个POST请求,开发者可以在响应包中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。
4.3.2假如服务器无法保证在五秒内处理并回复,必须直接回复success,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。
4.3.3一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
1. 开发者在5秒内未回复任何内容
2. 开发者回复了异常数据,比如JSON数据等
4.3.4返回的XML格式为:
<xml>
#用户账号(OpenID)
12345678#消息创建时间
#消息类型(文本)
#消息内容
</xml>
4.4代码实现:
fromflaskimportFlask,request,abort,render_template
importhashlib
importxmltodict
importtime
importurllib2
importjson
# 常量
# 微信的token令牌
WECHAT_TOKEN ="itcast"
WECHAT_APPID ="wx36766f74dbfeef15"
WECHAT_APPSECRET ="aaf6dbca95a012895eb570f0ba549ee5"
app = Flask(__name__)
@app.route("/wechat8000",methods=["GET","POST"])
defwechat():
"""对接微信公众号服务器"""
# 接收微信服务器发送的参数
signature = request.args.get("signature")
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
# 校验参数
if notall([signature,timestamp,nonce]):
abort(400)
# 按照微信的流程进行计算签名
li = [WECHAT_TOKEN,timestamp,nonce]
# 排序
li.sort()
# 拼接字符串
tmp_str ="".join(li)
# 进行sha1加密, 得到正确的签名值
sign = hashlib.sha1(tmp_str).hexdigest()
# 将自己计算的签名值与请求的签名参数进行对比,如果相同,则证明请求来自微信服务器
ifsignature != sign:
# 表示请求不是微信发的
abort(403)
else:
# 表示是微信发送的请求
ifrequest.method =="GET":
# 表示是第一次接入微信服务器的验证
echostr = request.args.get("echostr")
if notechostr:
abort(400)
returnechostr
elifrequest.method =="POST":
# 表示微信服务器转发消息过来
xml_str = request.data
if notxml_str:
abort(400)
# 对xml字符串进行解析
xml_dict = xmltodict.parse(xml_str)
xml_dict = xml_dict.get("xml")
# 提取消息类型
msg_type = xml_dict.get("MsgType")
ifmsg_type =="text":
# 表示发送的是文本消息
# 构造返回值,经由微信服务器回复给用户的消息内容
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType":"text",
"Content": xml_dict.get("Content")
}
}
else:
resp_dict = {
"xml": {
"ToUserName": xml_dict.get("FromUserName"),
"FromUserName": xml_dict.get("ToUserName"),
"CreateTime": int(time.time()),
"MsgType":"text",
"Content":"i love u"
}
}
# 将字典转换为xml字符串
resp_xml_str = xmltodict.unparse(resp_dict)
# 返回消息数据给微信服务器
returnresp_xml_str
五、微信网页授权
5.1概念:当微信内网页需要用到用户的个人信息时,需要用户进行授权。
5.2授权的步骤:
1. 第一步:用户同意授权,获取code
2. 第二步:通过code换取网页授权access_token
3. 第三步:拉取用户信息(需scope为 snsapi_userinfo)
5.3授权流程:
5.3.1设置网页授权回调域名:
在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的开发者中心页配置授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL
5.3.2用户同意授权,获取code
5.3.2.1让用户访问一下链接地址:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
5.3.2.2地址参数分析:
appid:公众号的唯一标识
redirect_uri:授权后重定向的回调链接地址,请使用urlencode对链接进行处理
response_type:返回类型,固定填写code
scope:填写snsapi_userinfo表示页面可通过OpenID获取用户详细信息
state:重定向后会带上state参数,可填可不填
#wechat_redirect:最后必须带此参数
5.3.2.3下图为scope等于snsapi_userinfo时的授权页面:
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
5.3.2.4通过code换取网页授权access_token:
请求方法:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
5.3.2.5参数分析:
5.3.2.6返回值:
正确时返回的JSON数据包如下:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
错误时返回的JSON数据包如下:
{
"errcode":40029,
"errmsg":"invalid code"
}
5.3.2.7拉取用户信息(需scope为 snsapi_userinfo):
请求方法:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
5.3.2.8参数分析:
5.3.2.9返回值:
正确时返回的JSON数据包如下:
{
"openid":" OPENID",
"nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "头像地址",
"privilege":[
"PRIVILEGE1"
"PRIVILEGE2"
],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
5.3.2.10 JSON数据分析:
5.4用户实际访问的页面地址:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx36766f74dbfeef15&redirect_uri=http%3A//www.itcastcpp.cn/wechat8000/index
&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
注意:需要使用urllib.quote()函数对redirect_uri进行转码
5.5代码实现:
#www.itcastcpp.cn/wechat8000/index
@app.route("/wechat8000/index")
defindex():
"""让用户通过微信访问的网页页面视图"""
# 从微信服务器中拿去用户的资料数据
# 1. 获取code参数
code = request.args.get("code")
if notcode:
returnu"缺失code参数"
# 2. 向微信服务器发送http请求,获取access_token
url ="https://api.weixin.qq.com/sns/oauth2/access_token? appid=%s&secret=%s&code=%s&grant_type=authorization_code"
\% (WECHAT_APPID,WECHAT_APPSECRET,code)
# 使用urllib2的urlopen方法发送请求
# 如果只传网址url参数,则默认使用http的get请求方式, 返回响应对象
response = urllib2.urlopen(url)
# 获取响应体数据,微信返回的json数据
json_str = response.read()
resp_dict = json.loads(json_str)
# 提取access_token
if"errcode"inresp_dict:
returnu"获取access_token失败"
access_token = resp_dict.get("access_token")
open_id = resp_dict.get("openid")# 用户的编号
# 3. 向微信服务器发送http请求,获取用户的资料数据
url ="https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN"\
% (access_token,open_id)
response = urllib2.urlopen(url)
# 读取微信传回的json的响应体数据
user_json_str = response.read()
user_dict_data = json.loads(user_json_str)
if"errcode"inuser_dict_data:
returnu"获取用户信息失败"
else:
# 将用户的资料数据填充到页面中
returnrender_template("index.html",user=user_dict_data)