您好!欢迎来到北极熊

北极熊

热门搜索: 任正非传    神雕侠侣    红楼梦   

扫码登录的简单实现

分类:技术交流 时间:2021-09-23 21:49 浏览:450
概述
前言本文将介绍基于SpringBoot + Vue + Android实现的扫码登录demo的总体思路,完整代码已上传到GitHub。Web端体验地址:http://47.116.72.33/(只剩一个月有效期),apk下载地址:https://github.com/zhangjiwei1221/qrscan/releases/tag/0.0.1。用户名:非空即可,密码:123456,效果见文末,整体实现如有不妥之处,欢迎交流讨论,实现
内容

前言

本文将介绍基于SpringBoot + Vue + Android实现的扫码登录demo的总体思路,完整代码已上传到GitHub。Web端体验地址:http://47.116.72.33/(只剩一个月有效期),apk下载地址:https://github.com/zhangjiwei1221/qrscan/releases/tag/0.0.1。用户名:非空即可,密码:123456,效果见文末,整体实现如有不妥之处,欢迎交流讨论,实现部分参考二维码扫码登录是什么原理。


项目简介

后端:SpringBoot,Redis。

前端:Vue,Vue Router、VueX、Axios、vue-qr、ElemntUI。

安卓:ZXing、XUI、YHttp。


实现思路

总体的扫码登录和OAuth2.0的验证逻辑相似,如下所示:



用户选择扫码登录可以看作是A:前端发授权请求,等待app扫码。

用户使用app进行扫码可以看作是B:扫码进行授权,返回一个临时Token供二次认证。

用户在app进行确认登录可以看作是C:进行登录确认,授权用户在Web端登录。

后端在用户确认登录后返回一个正式Token即可看作是步骤D。

后续前端根据正式Token访问后台接口,正式在Web端进行操作即可看作是E和F。


二次认证的原因


之所以在用户扫码之后还需要进行再一次的确认登录,而不是直接就登录的原因,则是为了用户安全考虑,避免用户扫了其他人需要登录的二维码,在未经确认就直接登录了,导致他人可能会在我们不知道的情况下访问我们的信息。


实现步骤

用户访问网页端,选择扫码登录


用户在选择扫码登录时,会向后端发送一个二维码的生成请求,后端生成UUID,并保存到Redis(固定有效时间),状态设置为UNUSED(未使用)状态,如果Redis缓存过期,则为EXPIRE(过期)状态,前端根据后端返回的内容生成二维码,并设置一个定时器,每隔一段时间根据二维码的内容中的UUID,向后端发送请求,获取二维码的状态,更新界面展示的内容。


生成二维码后端接口:


/**

 * 生成二维码内容

 *

 * @return 结果

 */

@GetMapping("/generate")

public BaseResult generate() {

    String code = IdUtil.simpleUUID();

    redisCache.setCacheObject(code, CodeUtils.getUnusedCodeInfo(), 

                              DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS);

    return BaseResult.success(GENERATE_SUCCESS, code);

}


前端获取内容,生成二维码:


getToken() {

    this.codeStatus = 'EMPTY'

    this.tip = '正在获取登录码,请稍等'

    // 有效时间 60 秒

    this.effectiveSeconds = 60

    clearInterval(this.timer)

    request({

        method: 'get',

        url: '/code/generate'

    }).then((response) => {

        // 请求成功, 设置二维码内容, 并更新相关信息

        this.code = `${HOST}/code/scan?code=${response.data}`

        this.codeStatus = 'UNUSED'

        this.tip = '请使用手机扫码登录'

        this.timer = setInterval(this.getTokenInfo, 2000)

    }).catch(() => {

        this.getToken()

    })

}


使用手机扫码,二维码状态改变


当用户使用手机扫码时(已登录并且为正确的app,否则扫码会跳转到自定义的宣传页),会更新二维码的状态为CONFIRMING(待确认)状态,并在Redis缓存中新增用户名及头像信息的保存供前端使用展示,此外还会返回用户的登录信息(登录地址、浏览器、操作系统)给app展示,同时生成一个临时Token给app(固定有效时间)。


用户扫码时的后台处理:


/**

 * 处理未使用状态的二维码

 *

 * @param code 二维码

 * @param token token

 * @return 结果

 */

private BaseResult handleUnusedQr(String code, String token) {

    // 校验 app 端访问传递的 token

    boolean isLegal = JwtUtils.verify(token);

    if (!isLegal) {

        return BaseResult.error(AUTHENTICATION_FAILED);

    }

    // 保存用户名、头像信息, 供前端展示

    String username = JwtUtils.getUsername(token);

    CodeVO codeVO = CodeUtils.getConfirmingCodeInfo(username, DEFAULT_AVATAR_URL);

    redisCache.setCacheObject(code, codeVO, DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS);

    // 返回登录地址、浏览器、操作系统以及一个临时 token 给 app

    String address = HttpUtils.getRealAddressByIp();

    String browser = HttpUtils.getBrowserName();

    String os = HttpUtils.getOsName();

    String tmpToken = JwtUtils.sign(username);

    // 将临时 token 作为键, 用户名为内容存储在 redis 中

    redisCache.setCacheObject(tmpToken, username, DEFAULT_TEMP_TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);

    LoginInfoVO loginInfoVO = new LoginInfoVO(address, browser, os, tmpToken);

    return BaseResult.success(SCAN_SUCCESS, loginInfoVO);

}


手机确认登录


当用户在app中点击确认登录时,就会携带生成的临时Token发送更新状态的请求,二维码的状态会被更新为CONFIRMED(已确认登录)状态,同时后端会生成一个正式Token保存在Redis中,前端在轮询更新状态时获取这个Token,然后使用这个Token进行登录。


后端处理确认登录的代码:


/**

 * 处理未待确认状态的二维码

 *

 * @param code 二维码

 * @param token token

 * @return 结果

 */

private BaseResult handleConfirmingQr(String code, String token) {

    // 使用临时 token 获取用户名, 并从 redis 中删除临时 token

    String username = redisCache.getCacheObject(token);

    if (StringUtils.isBlank(username)) {

        return BaseResult.error(AUTHENTICATION_FAILED);

    }

    redisCache.deleteObject(token);

    // 根据用户名生成正式 token并保存在 redis 中供前端使用

    String formalToken = JwtUtils.sign(username);

    CodeVO codeVO = CodeUtils.getConfirmedCodeInfo(username, DEFAULT_AVATAR_URL, formalToken);

    redisCache.setCacheObject(code, codeVO, DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS);

    return BaseResult.success(CONFIRM_SUCCESS);

}


效果演示

在这里插入图片描述

在这里插入图片描述

评论
资讯正文页右侧广告
联系我们
电话:18936411277
邮箱:1044412291@qq.com
时间:09:00 - 19:00
公众号:北格软件
底部广告