zengfanpei 2020-02-21
01 公众号接入
01.1 注册并登录微信公众平台。在公众平台首页最下面,找到开发/基本配置,记下开发者ID和开发者密码。
02.2 编写服务端程序
在WeChart APP下创建目录views_wechart,并创建文件constants.py文件:
# 微信的token令牌 WECHAT_TOKEN = "tongheng2" # 开发者ID WECHAT_APPID = "wxe2a552d82fd68c2a" # 开发者密码 WECHAT_APPSECRET = "cbc983eddcf8b6223680abfbfd86933e" # 微信服务器IP地址 SERVER_ID_ADDRESS = ‘111.229.48.106‘ # 缓存公众号access_token WECHATPY_ACCESS_TOKEN_CACHE = ‘redis://:‘ + SERVER_ID_ADDRESS + ‘:6379/3‘ # 测试回调链接地址 REDIRECT_URI = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/wechatpyoauth/‘ # 注册页面 REGISTER_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/RegisterView/‘ # 管理员首页 ADMIN_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/AdminHomeView/‘ # 药师首页 YAOSHI_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/YaoshiHomeView/‘ # 用户首页 USER_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/UserHomeView/‘ # 医生首页 DOCTOR_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/DoctorHomeView/‘ # 医生助理首页 DOCTOR_ASSISTANT_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/DoctorAssistantHomeView/‘ # 财务首页 CAIWU_HOME_ADDRESS = ‘http://‘ + SERVER_ID_ADDRESS + ‘/ThirdParts/CaiwuHomeView/‘
02.3 在views_wechart目录下创建utils.py文件
import hashlib
from wechatpy.oauth import WeChatOAuth
from wechatpy.client import WeChatClient
from wechatpy.session.redisstorage import RedisStorage
from redis import Redis
from . import constants
def generate_signature(timestamp, nonce):
"""
生成微信加密签名
:param timestamp: 时间戳
:param nonce: 随机数
:return: signature
"""
# 按照微信的流程进行计算签名
sortlist = [constants.WECHAT_TOKEN, timestamp, nonce]
# 排序
# 1)将token、timestamp、nonce三个参数进行字典序排序
sortlist.sort()
# 拼接字符串
tmp_str = "".join(sortlist)
# 进行sha1加密, 得到正确的签名值
# 2)将三个参数字符串拼接成一个字符串进行sha1加密
# signature = hashlib.sha1(tmp_str).hexdigest()
sha = hashlib.sha1()
sha.update(tmp_str.encode(‘utf-8‘))
signature = sha.hexdigest()
return signature
def get_WeChatOAuth(redirect_uri, state=‘123‘, scope=‘snsapi_userinfo‘):
"""
获取WeChatOAuth对象
:param redirect_uri: 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
:param scope:应用授权作用域,snsapi_base,snsapi_userinfo
:param state:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
:return: WeChatOAuth对象
"""
return WeChatOAuth(
app_id=constants.WECHAT_APPID,
secret=constants.WECHAT_APPSECRET,
redirect_uri=redirect_uri,
scope=scope,
state=state
)
def get_WeChatClient():
"""
获取WeChatClient对象
:return:WeChatClient对象
"""
redis_client = Redis.from_url(constants.WECHATPY_ACCESS_TOKEN_CACHE)
session_interface = RedisStorage(
redis_client,
prefix="wechatpy"
)
return WeChatClient(
constants.WECHAT_APPID,
constants.WECHAT_APPSECRET,
session=session_interface
)
def get_authorize_url(redirect_uri, state):
"""
获取授权跳转地址
:return: authorize_url
"""
wechatOAuth = get_WeChatOAuth(redirect_uri, state)
return wechatOAuth.authorize_url02.4 在views_chart下创建文件Interface.py文件
from rest_framework import status
import logging
from django.http import HttpResponse
from wechatpy.utils import check_signature
from wechatpy.exceptions import InvalidSignatureException
from wechatpy.replies import TextReply
from wechatpy import parse_message
from django.views.generic import View
from . import constants
from .utils import get_authorize_url
# 获取在配置文件中定义的logger,用来记录日志
logger = logging.getLogger(‘tongheng‘)
class WeChatPy(View):
"""
微信公众号开发服务器配置
"""
# http://127.0.0.1:8000/ThirdParts/wechatpy/?signature=d8e259ac87cf08ea6924fbb78479bb1fcf53d4d6&echostr=1406649499520832760×tamp=1581421688&nonce=929463593
# GET /ThirdParts/wechatpy/?signature=d8e259ac87cf08ea6924fbb78479bb1fcf53d4d6&echostr=1406649499520832760×tamp=1581421688&nonce=929463593
@classmethod
def get(cls, request):
"""
微信服务器验证消息
:param request:GET请求携带参数(signature、timestamp、nonce、echostr)
:return:原样返回echostr参数
"""
# logger.info("body:%s" % request.body)
# logger.info("GET:%s" % request.GET)
# logger.info("POST:%s" % request.POST)
# 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
signature = request.GET.get(‘signature‘)
# 时间戳
timestamp = request.GET.get(‘timestamp‘)
# 随机数
nonce = request.GET.get(‘nonce‘)
# 随机字符串
echostr = request.GET.get(‘echostr‘)
# logger.info("signature:%s" % signature)
# logger.info("timestamp:%s" % timestamp)
# logger.info("nonce:%s" % nonce)
# logger.info("echostr:%s" % echostr)
# 校验参数
if not all([signature, timestamp, nonce, echostr]):
# 请求参数错误
return HttpResponse(status.HTTP_400_BAD_REQUEST)
try:
check_signature(constants.WECHAT_TOKEN, signature, timestamp, nonce)
except InvalidSignatureException as e:
# 处理异常情况或忽略
logger.error(e)
# 微信签名错误
return HttpResponse(status.HTTP_403_FORBIDDEN)
# 验证成功时,应原样返回 echostr 参数值
return HttpResponse(echostr)
# http://127.0.0.1:8000/ThirdParts/wechatpy/
# POST /ThirdParts/wechatpy/
@classmethod
def post(cls, request):
"""
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
"""
# logger.info("body:%s" % request.body)
# logger.info("GET:%s" % request.GET)
# logger.info("POST:%s" % request.POST)
# 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
signature = request.GET.get(‘signature‘)
# 时间戳
timestamp = request.GET.get(‘timestamp‘)
# 随机数
nonce = request.GET.get(‘nonce‘)
# openid
openid = request.GET.get(‘openid‘)
# logger.info("signature:%s" % signature)
# logger.info("timestamp:%s" % timestamp)
# logger.info("nonce:%s" % nonce)
# openid:o-dlPwDD8x_tKgeinvgj9CY9sfSI
# logger.info("openid:%s" % openid)
# 校验参数
if not all([signature, timestamp, nonce, openid]):
# 请求参数错误
return HttpResponse(status.HTTP_400_BAD_REQUEST)
try:
check_signature(constants.WECHAT_TOKEN, signature, timestamp, nonce)
except InvalidSignatureException as e:
# 处理异常情况或忽略
logger.error(e)
# 微信签名错误
return HttpResponse(status.HTTP_403_FORBIDDEN)
xml = request.body
logger.info("xml:%s" % xml)
if not xml:
# 请求参数错误
return HttpResponse(status.HTTP_400_BAD_REQUEST)
msg = parse_message(xml)
msg_type = msg.type
# logger.info("msg type:%s" % type(msg))
# logger.info("msg_type:%s" % msg_type)
msg_content = None
if msg_type == "event":
# 推送事件
if msg.event == "subscribe":
# 关注公众号
authorize_url = get_authorize_url(constants.REGISTER_HOME_ADDRESS, ‘subscribe‘)
msg_content = ‘感谢你关注,请点击<a href="‘ + authorize_url + ‘">注册</a>‘
elif msg_type == "text":
# 文本消息
if msg.content == "注册":
authorize_url = get_authorize_url(constants.REGISTER_HOME_ADDRESS, ‘register‘)
msg_content = ‘请点击<a href="‘ + authorize_url + ‘">注册</a>‘
if msg_content is not None:
reply = TextReply(content=msg_content, message=msg)
# 转换成 XML
xml = reply.render()
return HttpResponse(xml)
else:
return HttpResponse(‘‘)