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后台启动的线程过多,阻塞其他接口。

  • 16
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,如果你只支持微信扫码登录,那么密码这个字段可以不需要。可以将原来的password字段删除。同时,在save方法中,也不需要对密码进行加密。 以下是新的User模型的代码: ```python from django.db import models class User(models.Model): name = models.CharField(max_length=50, verbose_name='姓名') email = models.EmailField(unique=True, verbose_name='邮箱') is_active = models.BooleanField(default=False, verbose_name='是否激活') is_superuser = models.BooleanField(default=False, verbose_name='是否超级管理员') avatar = models.ImageField(upload_to='avatars/', null=True, blank=True, verbose_name='头像') wechat_id = models.CharField(max_length=50, null=True, blank=True, verbose_name='微信ID') wechat_qrcode = models.ImageField(upload_to='qrcodes/', null=True, blank=True, verbose_name='微信二维码') class Meta: verbose_name = '用户' verbose_name_plural = verbose_name def __str__(self): return self.name def save(self, *args, **kwargs): if not self.pk: # 新建用户时,发送激活邮件 self.send_activation_email() # 调用父类的save方法保存用户信息 super().save(*args, **kwargs) def send_activation_email(self): # 发送激活邮件 subject = '请激活您的账号' message = f'请点击链接激活账号:{settings.SITE_URL}{reverse("activate", args=[self.pk])}' from_email = settings.DEFAULT_FROM_EMAIL recipient_list = [self.email] send_mail(subject, message, from_email, recipient_list) ``` 在新的User模型中,我们删除了password字段,并删除了save方法中的密码加密代码。同时,在save方法中,如果是新建用户,则发送激活邮件。 这样,我们就设计出了一个支持微信扫码登录的用户表。需要注意的是,微信扫码登录需要调用微信开放平台的API,这部分的实现需要在视图中进行。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值