java编程之基于SpringBoot框架实现扫码登录

作者:庄周de蝴蝶 时间:2023-02-14 02:39:28 

完整代码已上传到GitHub。

Web端体验地址:http://47.116.72.33/(只剩一个月有效期)

apk下载地址:https://github.com/zhangjiwei1221/qrscan/releases/tag/0.0.1。

用户名:非空即可,密码:123456,效果见文末,整体实现如有不妥之处,欢迎交流讨论

实现部分参考二维码扫码登录是什么原理。

项目简介

后端:SpringBootRedis
前端:VueVue RouterVueXAxiosvue-qrElemntUI
安卓:ZXingXUIYHttp

实现思路

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

java编程之基于SpringBoot框架实现扫码登录

用户选择扫码登录可以看作是A:前端发授权请求,等待app扫码。
用户使用app进行扫码可以看作是B:扫码进行授权,返回一个临时Token供二次认证。
用户在app进行确认登录可以看作是C:进行登录确认,授权用户在Web端登录。
后端在用户确认登录后返回一个正式Token即可看作是步骤D
后续前端根据正式Token访问后台接口,正式在Web端进行操作即可看作是EF

二次认证的原因

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

实现步骤

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

用户在选择扫码登录时,会向后端发送一个二维码的生成请求,后端生成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()
   })
}

后端返回二维码状态信息的接口:


/**
* 获取二维码状态信息
*
* @param code 二维码
* @return 结果
*/
@GetMapping("/info")
public BaseResult info(String code) {
   CodeVO codeVO = redisCache.getCacheObject(code);
   if (codeVO == null) {
       return BaseResult.success(INVALID_CODE, StringUtils.EMPTY);
   }
   return BaseResult.success(GET_SUCCESS, codeVO);
}

前端轮询获取二维码状态:


getTokenInfo() {
   this.effectiveSeconds--
   // 二维码过期
   if (this.effectiveSeconds <= 0) {
       this.codeStatus = 'EXPIRE'
       this.tip = '二维码已过期,请刷新'
       return
   }
   // 轮询查询二维码状态
   request({
       method: 'get',
       url: '/code/info',
       params: {
           code: this.code.substr(this.code.indexOf('=') + 1)
       }
   }).then(response => {
       const codeVO = response.data
       // 二维码过期
       if (!codeVO || !codeVO.codeStatus) {
           this.codeStatus = 'EXPIRE'
           this.tip = '二维码已过期,请刷新'
           return
       }
       // 二维码状态为为正在登录
       if (codeVO.codeStatus === 'CONFIRMING') {
           this.username = codeVO.username
           this.avatar = codeVO.avatar
           this.codeStatus = 'CONFIRMING'
           this.tip = '扫码成功,请在手机上确认'
           return
       }
       // 二维码状态为确认登录
       if (codeVO.codeStatus === 'CONFIRMED') {
           clearInterval(this.timer)
           const token = codeVO.token
           store.commit('setToken', token)
           this.$router.push('/home')
           Message.success('登录成功')
           return
       }
   })
}

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

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

用户扫码时的后台处理:


/**
* 处理未使用状态的二维码
*
* @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);
}

效果演示

java编程之基于SpringBoot框架实现扫码登录

java编程之基于SpringBoot框架实现扫码登录

以上就是java编程基于SpringBoot框架实现扫码登录的详细内容,更多关于java编程SpringBoot框架实现扫码登录的资料请关注脚本之家其它相关文章!

来源:https://blog.csdn.net/qq_41698074/article/details/120405575

标签:SpringBoot,框架,扫码登录
0
投稿

猜你喜欢

  • android使用flutter的ListView实现滚动列表的示例代码

    2023-06-26 09:00:13
  • Java 深入浅出掌握Collection单列集合Set

    2023-10-07 18:49:32
  • 详解Spring Bean的集合注入和自动装配

    2023-02-18 15:02:58
  • Spring Boot2.x如何自定义Endpoint

    2023-03-05 18:04:12
  • Java关键字详解之final static this super的用法

    2022-01-19 09:24:39
  • Spring Boot项目如何同时支持HTTP和HTTPS协议的实现

    2023-11-19 19:57:05
  • springBoot加入thymeleaf模板的方式

    2023-11-25 14:31:23
  • Java SE求解汉诺塔问题的示例代码

    2022-05-10 23:44:30
  • 关于springboot配置文件密文解密方式

    2023-11-09 04:21:24
  • C#复制数组的两种方式及效率比较

    2023-07-15 04:19:12
  • Java并发编程示例(七):守护线程的创建和运行

    2023-11-25 11:39:32
  • Spring MVC+FastJson+hibernate-validator整合的完整实例教程

    2021-10-31 13:20:13
  • Spring Boot + thymeleaf 实现文件上传下载功能

    2022-05-22 03:56:13
  • Java8 如何正确高效的使用并行流

    2021-06-01 20:29:19
  • 简单了解Spring中常用工具类

    2021-09-24 10:44:28
  • 你都理解创建线程池的参数吗?

    2022-06-10 06:36:05
  • 详解Struts2动态方法调用

    2022-10-18 11:19:25
  • mybatis-plus 返回部分字段的解决方式

    2023-03-25 14:45:04
  • 一篇文章带你了解XGBoost算法

    2021-10-10 01:14:45
  • iOS实现从背景图中取色的代码

    2023-07-06 15:18:32
  • asp之家 软件编程 m.aspxhome.com