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_url
02.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(‘‘)