django实现微信公众号扫码登录
首先是去获取access_token,access_token接口有次数限制,所以保存到缓存,失效时再去调用接口
import base64 import json import time import traceback import requests from django.core.cache import cache from rest_framework.response import Response from rest_framework.views import APIView app_id = 'xxxxxxx' # appid app_secret = 'xxxxxx' # app密钥 msg_key = 'xxxxxx' # 消息模板id app_token = "xxxxxxx" # apptoken def get_wx_access_token(): """ 获取access_token,保存到缓存 :return: """ url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}'.format(app_id, app_secret) resp = requests.get(url) # print(resp.json()) access_token = resp.json()['access_token'] cache.set('wx_access_token', access_token, 60*60*2) # print(access_token) return access_token def get_ticket(access_token, scene_str=''): """ 获取ticket :param access_token: :param scene_str: :return: """ url = 'https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}'.format(access_token) # print(scene_id) data = { "expire_seconds": 604800, "action_name": "QR_STR_SCENE", "action_info": {"scene": {'scene_str': scene_str}} # 一定要取字符串,整形有最大值限制 } resp = requests.post(url, data=json.dumps(data)) # print(resp.json()) ticket = resp.json()['ticket'] return ticket def get_code_img(ticket): """ 获取二维码,保存位图片 :param ticket: :return: """ url = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' + ticket resp = requests.get(url) # print(resp.content) img = base64.b64encode(resp.content).decode('ascii') fileData = base64.urlsafe_b64decode(img.encode('UTF-8')) with open('thefilename.png', 'wb') as theFile: theFile.write(fileData) # print(img) return img from rest_framework.throttling import AnonRateThrottle # 限制调用次数 class CustomAnonRateThrottle(AnonRateThrottle): THROTTLE_RATES = {"anon": "5/min"} # 获取二维码 class WxGetCode(APIView): throttle_classes = [CustomAnonRateThrottle] def get(self, request): # cache.delete('wx_access_token') # body = request.query_params.dict() # scene_id = get_json_values('scene_id', body, 1) scene_str = str(int(time.time()*100000)) # print(scene_str) access_token = cache.get('wx_access_token') if cache.get('wx_access_token') else get_wx_access_token() ticket = get_ticket(access_token, scene_str) # code_img = get_code_img(ticket) code_url = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' + ticket resp = {'data': code_url, 'sceneStr': scene_str} return Response(resp)
下面get请求是公众号回调时需要验证token的接口,post请求是扫码完成以后回调的接口
微信回调的是xml形式,需要让django允许解析xml格式参数
import datetime import hashlib import time import traceback import requests from django.core.cache import cache from django.http import HttpResponse from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_xml.parsers import XMLParser from wxgetcode import get_wx_access_token, app_token, msg_key class TextXMLParser(XMLParser): media_type = 'text/xml' # class weixin(APIView): parser_classes = (TextXMLParser,) def get(self, request): # 自己写的授权 # print(request.method) data = request.GET # print(data) signature = data.get("signature") # print(signature) if not signature: return HttpResponse("dasdfafd") # print("sign", signature) timestamp = data.get("timestamp") nonce = data.get("nonce") echostr = data.get("echostr") # token 为用户在微信公众平台自定义token token = app_token # 将token、timestamp、nonce三个参数进行字典序排序 list = [token, timestamp, nonce] list.sort() # 将三个参数字符串拼接成一个字符串进行sha1加密 info = "".join(list) sha1 = hashlib.sha1() sha1.update(info.encode()) hashcode = sha1.hexdigest() # 比对相同就返回 echostr if hashcode == signature: return HttpResponse(echostr) else: return "" def post(self, request): param = request.query_params.dict() # print(param) dic = request.data # EventKey场景码,获取二维码时返回的sceneStr EventKey = dic.get('EventKey') if dic.get('MsgType') == 'event': if dic.get('Event') == 'subscribe': # print('新用户') # parse_subscribe(dic) # 新关注用户扫码 access_token = cache.get('wx_access_token') if cache.get( 'wx_access_token') else get_wx_access_token() open_id = param.get('openid') # print(access_token) # print(open_id) url = f"https://api.weixin.qq.com/cgi-bin/user/info?access_token={access_token}&openid={open_id}&lang=zh_CN" res = requests.get(url) # print(res.json()) res = res.json() unionid = res.get('unionid') # 处理逻辑 pass # 完成登录公众号推送消息 send_wx_msg(access_token, open_id) elif dic.get('Event') == 'SCAN': # print('老用户') access_token = cache.get('wx_access_token') if cache.get('wx_access_token') else get_wx_access_token() open_id = param.get('openid') # print(access_token) # print(open_id) url = f"https://api.weixin.qq.com/cgi-bin/user/info?access_token={access_token}&openid={open_id}&lang=zh_CN" res = requests.get(url) # print(res.json()) res = res.json() unionid = res.get('unionid') # parse_scan(dic) # 已经关注用户扫码 # 处理逻辑 pass # 完成登录公众号推送消息 send_wx_msg(access_token, open_id) resp = 'success' return HttpResponse(resp) def send_wx_msg(ACCESS_TOKEN, openid): """ 消息推送,根据消息模板的定义传值即可 :param ACCESS_TOKEN: :param openid: :return: """ url = f'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={ACCESS_TOKEN}' data = { "touser": openid, "template_id": msg_key, "url": "http://weixin.qq.com/download", "topcolor": "#FF0000", "data": { 'character_string3': {'value': 'xxxxxxx', "color": "#173177"}, 'thing2': {'value': 'xxx', "color":"#173177"}, 'keyword2': {'value': 'ssss', "color": "#173177"}, 'time4': {'value': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "color": "#173177"}, 'keyword5': {'value': 'ssss', "color": "#173177"}, } } res = requests.post(url, json=data) # print(res.json())