扫码登陆的实现方案

扫码登陆的实现方案

思路

最近准备将小程序功能迁移到第一版的PC端上来,遇到第一个问题就是用户登陆,因为是个人小程序,授权登陆我只能获取openID,并没有手机号等相关信息,所以打通用户成了一个问题,最初想的是让用户在小程序上绑定手机号,但是最终觉得太麻烦,于是就想到扫码登陆,于是就去查资料,小程序API中是提供扫码的API,那么第一步就走通了,剩下就是实现了。经过百度一系列的文章参考。

操作上面就三步:

  • 第一步使用Java生成二维码
  • 第二步将二维码展现的Html上面
  • 第三步使用小程序扫码

但是实现过程遇到挺多的问题:

第一个问题,如何生成二维码?

这里采用google公司的zxing来生成二维码。

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.3</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.3</version>
        </dependency>

第二个问题,生成二维码应该带有哪些信息,能让小程序扫码
确认登陆之后找到这个二维码的对应的浏览器呢?

这里就和前端有一定的关系了,当用户点击登陆的时候,判断浏览器是否支持WebSocket,如果支持就连接WebSocket,然后WebSocket接口返回一个UUID,该UUId当作这台浏览器的身份(API上说一台机器上生成的UUID用完一百亿年,所以不考虑重复),然后前端再通过这个UUID作为参数获取二维码,后台将UUID存入到二维码中,当小程序扫码获取该UUID之后,如果确认登陆那么发送请求到后台,后台通过UUID找到对应会话,然后通知前端登陆成功。

如果不支持WebSocket,可以采用轮询的方式询问服务端,该UUID是否确认登陆。

        //获得消息事件
        socket.onmessage = function (msg) {
            var obj = JSON.parse(msg.data);
            console.log(obj)
            if (obj) {
                if (obj.status == 200) {
                //登陆成功
                   sessionStorage.setItem('inc-token', obj.data);
                    $("#info").html(obj.info)
                    alertInfoFun("感谢您的登录")
                } else {
                    Token = obj.data;
                    //展示二维码
                    document.getElementById("img").src = "http://127.0.0.1:8081/xcx/qr/create/" + Token;
                }
            }
        };

第三个问题,前端如何获取二维码并展示

一种是将生成二维码保存成文件,前端获取链接。

另一种将二维码转成流,前端直接获取,不用存。我这里采用第二种方式

结合第二个问题,这里将Token存入到二维码中

    //将OutputStream通过接口write回去即可
    private static void generate(String token, OutputStream stream) {
        try {
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            FiveElements fiveElements = new FiveElements();
            fiveElements.setToken(token);
            fiveElements.setDate(new Date());
            String contents = JSON.toJSONString(fiveElements);
            HashMap<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, CHARTSET);
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
            hints.put(EncodeHintType.MARGIN, 2);
            BitMatrix bitMatrix = multiFormatWriter.encode(contents, BarcodeFormat.QR_CODE, 400, 400, hints);
            MatrixToImageWriter.writeToStream(bitMatrix, "jpg", stream);
        } catch (Exception e) {
            System.out.println("二维码生成出错" + e.getMessage());
        }
    }

第四个问题登陆的时效性

我这边采用JWT对openID加密生成Token的作为请求Header的参数,该Token的时效性只有半个小时,半个小时候后,必须重新登陆(大家可以自己考虑时间,我是想到我的小程序从来没人能玩半个小时,所以定为半个小时的时效性)

实现效果

上一部分我们其实已经说了实现的过程,这一部分就不重点说明,源码放到GitHub上,大家自行下载参考

https://github.com/xynuSuMu/QR-Demo.git

其中根目录的qr.html是前端展示二维码的页面。


版权声明:本文为qq442270636原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。