微信扫描登录,正统的做法是通过微信开放平台,申请网页接入。
由于自己已有一个认证过的微信公众号,所以尝试下能否通过公众号已有的接口做,结果证明是可以的。
上步骤分享下吧:
1. 访问“/login”路由,后台调用微信生成临时带参数的二维码接口,生成临时二维码传给前端网页,供用户扫描。
url.py
url('^login/$', LoginView.as_view(), name='login'),
view.py
class LoginView(View): def get(self, request): uid = uuid.uuid1() ticket = wpManager.createShortTicket("login_{}".format(uid))["ticket"] qrc = wpManager.getQrCode(ticket) return render(request, 'login.html', locals())
每次刷新登录页面,都会生成一个唯一的uuid,然后生成二维码的时候,把这个uuid传入微信的scene字段中。
def createShortTicket(self, scene_str): url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}".format(self.getAccessToken()) data = { "expire_seconds": 300, "action_name": "QR_STR_SCENE", "action_info": { "scene": { "scene_str": scene_str } } } ret = self.s.post(url=url, data=json.dumps(data)) return json.loads(ret.content)
2. 用户扫描后,公众号接口处理。
用户扫描二维码后,你的服务器应该能收到微信的回调。如何配置,还是要看微信的文档。
回调的格式是一段xml,我自己写了个方法转成了json。
def xml2Json(xml): root = ET.fromstring(xml) ret = {} # 遍历xml文档的第二层 for child in root: # 第二层节点的标签名称和属性 ret[child.tag] = child.text # 遍历xml文档的第三层 for children in child: # 第三层节点的标签名称和属性 ret[child.tag] = child.text return ret
转换之后的json长这样:
{'ToUserName': 'gh_45fdd1acea96', 'FromUserName': 'oCpQ66MOa9KrYG2xE9mWwmuA8tgY', 'CreateTime': '1555652269', 'MsgType': 'event', 'Event': 'SCAN', 'EventKey': 'login_92a72c60-6264-11e9-b2bb-5254001e93b7', 'Ticket': 'XXXXXXXXXXXXXXXXXXXXXXXXX'}
我的处理方式是,取出这里的“EventKey”的值,看看是否有“login_”字符(因为可以用其他字符串标识其他的业务)。
如果有,就将后面的uuid作为key,用户的openid作为值,存入redis缓存,设置有效时间为5分钟。
if "login_" in data["EventKey"]: l = data["EventKey"].split("login_") cache.set(l[1], data["FromUserName"], 60 * 5) response = wechat_instance.response_text("微信登录认证成功") return HttpResponse(response)
3. 接下来要做的就简单了,在登录页面,设置ajax轮训,去后台查询缓存中是否有uuid这个key存在,如果存在,则取出openid,自动登录用户。
url('^login/checkqrc/$', LoginCheckQrcView.as_view(), name='checkqrc'),
login.html:
<script> var count = 1; function check() { $.ajax({ cache: false, type: 'get', dataType: 'json', url: '{% url "checkqrc" %}' + "?uid={{ uid }}", traditional: true, async: true, success: function (data) { if (data.status == "ok") { window.location.href = "/apply"; } else { count += 1; if (count >= 60) { clearInterval(checktimer); } } }, error: function (xhr, textStatus) { console.log(xhr); console.log(textStatus); } }); } check(); checktimer = setInterval(function () { check(); }, 10000); </script>
view.py.
class LoginCheckQrcView(View): def get(self, request): ret = {} uid = request.GET.get('uid', None) ret["status"] = "fail" count = 1 while True: if cache.has_key(uid): mpid = cache.get(uid) user, created = UserProfile.objects.get_or_create(mpid=mpid) user.backend = 'django.contrib.auth.backends.ModelBackend' login(request, user) ret["status"] = "ok" cache.delete(uid) logger.info("{}通过微信扫描登陆成功".format(user.username)) break else: count += 1 if count >= 10: break time.sleep(1) return HttpResponse(json.dumps(ret), content_type="application/json")
在这里我采用了长轮训的方法,每个ajax发来的请求,我都会在一个循环中等待,每隔1秒查询缓存。如果超过10秒,就返回fail.如果查询到有记录,则登录用户,并删除缓存中的key。
这样的好处是用户扫描后,网页能够马上收到响应,不会出现用户扫码了,还要等几秒网页才有反应。坏处就是如果请求登录的人数太多,会导致django后台启动的线程过多,阻塞其他接口。
原文链接:https://blog.csdn.net/u014633966/article/details/89398138