django实现PC网页通过微信扫码登录,只需要有个通过认证的微信公众号即可


微信扫描登录,正统的做法是通过微信开放平台,申请网页接入。

由于自己已有一个认证过的微信公众号,所以尝试下能否通过公众号已有的接口做,结果证明是可以的。

上步骤分享下吧:

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.py

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