现在很多PC端的网站为了用户登录方便都提供了扫描二维码实现用户登录的功能,通过移动端扫描PC端的二维码并且在移动端进行用户确认登录之后实现PC端网页的登录操作。而对于扫码登录来讲其实现方式也比较简单,下面我们就来基于SpringBoot实现一个扫描登录功能。
二维码生成
在SpringBoot项目中我们可以利用第三方库来自己生成二维码,或者是我们可以调用一些第三方的系统,例如微信、QQ、或者是其他平台来生成二维码的连接。下面我们演示如何使用Zxing来生成一个二维码,代码如下。
public class QRCodeLoginController {
private String generateQRCode(String loginToken) {
String qrCodeBase64 = null;
try {
// 设置二维码参数
int width = 300;
int height = 300;
String charset = "UTF-8";
String qrCodeData = "loginToken=" + loginToken;
// 使用zxing库生成二维码图片
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(qrCodeData, BarcodeFormat.QR_CODE, width, height);
// 将BitMatrix转换为BufferedImage
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
bufferedImage.createGraphics();
Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
graphics.setColor(Color.BLACK);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (bitMatrix.get(i, j)) {
graphics.fillRect(i, j, 1, 1);
}
}
}
// 将BufferedImage转换为Base64编码
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", baos);
byte[] bytes = baos.toByteArray();
qrCodeBase64 = Base64.getEncoder().encodeToString(bytes);
} catch (WriterException | IOException e) {
e.printStackTrace();
}
return qrCodeBase64;
}
}
生成随机唯一码
生成二维码之后,可以将二维码连接显示到登录页面上。这里需要注意,生成的二维码中一定要包含一个随机生成的唯一的用来完成登录操作的唯一标识,通过这个唯一标识来验证用户的登录操作,如下所示。
@GetMapping("/login")
public String getQRCode() {
// 生成随机登录标识符
loginToken = UUID.randomUUID().toString();
// 生成二维码图片
String qrCodeUrl = generateQRCode(loginToken);
return qrCodeUrl;
}
前端轮询调用
在很多时候开发者对于生成二维码,进行用户唯一ID校验都没有什么疑惑,唯独就对前端如何进行调用而产生了疑惑,到底是通过轮询的方式还是通过WebSocket的方式来进行接口验证调用来判断到底用户是否在移动端点击了授权。
这里我们提供了轮询的实现方式。在很多的网站实现扫码登录的时候都采用了这种方式,通过定时轮询检查用户是否在移动端点击了授权登录。
<script>
// 定时轮询检查登录状态
setInterval(function() {
$.get("/checkLogin", function(data) {
if (data === "Login success") {
// 登录成功,更新页面显示
$("#loginStatus").text("Login successful!");
}
});
}, 3000); // 每隔3秒钟检查一次登录状态
</script>
如果用户点击了授权登录的操作,那么当服务端用户状态发生了变化之后,PC端通过接口检查用户状态是否授权,来判断用户是否可以进入系统,授权成功,如下所示。
@GetMapping("/checkLogin")
public ResponseEntity<String> checkLoginStatus() {
if (userLoggedIn(loginToken)) {
// 用户已经扫码登录
return ResponseEntity.ok("Login success");
} else {
// 用户未扫码登录
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Login not completed");
}
}
private boolean userLoggedIn(String loginToken) {
// 检查用户是否已经扫码登录,根据登录标识符查询登录状态
// 返回 true 表示用户已经登录,否则返回 false
// 此处仅作示例,实际情况需要根据业务逻辑实现
return false;
}
对于移动端用户是否认证的功能判断需要根据用户具体的业务逻辑来实现,这里不做展示。
超时处理
如果在用户扫描过程中、或者是用户扫码完成之后点击了取消,那么这个时候PC端并不知道用户的这种操作,轮询调用检查还在继续,在这种情况下,如果对操作没有进行处理的话,就会导致网络资源的消耗。因为PC端一直在轮询检查用户状态。所以要对超时情况,异常情况等各种情况进行验证。
总结
上面我们介绍了关于如何实现PC端扫码登录的功能,整体流程分为了四个步骤。第一步、二维码的生成(包括随机登录标识等情况);第二步、前端登录检查实现;第三步、移动端调用服务器进行用户状态调整;第四步:超时或者是异常处理。当然这些操作在单机状态下实现起来都没有问题,但是在分布式、高并发情况下需要考虑的内容就比较多了。希望读者可以有所区别。