扫码登陆的实现方案
思路
最近准备将小程序功能迁移到第一版的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是前端展示二维码的页面。