实战剖析 App 扫码登录原理与实现(附源码)

某大型互联网公司JAVA技术经理,技术开发负责人,架构师;工作期间参与了公司整体架构的选型与设计,负责并完成了公司电商商城的建设,直播内容分享平台的建设等多个大型项目;开源技术爱好者;

文章正文

本场 Chat 将为您介绍,如何设计系统的 App 扫码登录,详细讲解其中的实现原理,与具体代码的实现过程。带您思考在实现过程中的需要注意的点,以及需要掌握的技巧。

本场 Chat 主要内容:

说明:文章中只是引用了部分的代码片段,完整的项目源码,请移步

https://gitee.com/GuoqingLee/scan-login-demo

1. App 扫码登录的原理,简单图例

大致的原理图如下:

原理图

时序图如下:

APP扫码登录时序图

这里用一个简单的时序图,介绍用户在使用 App 对 PC 端进行扫码的登录时,与 Server 端进行的交互的过程,以便于对整个过程有个初步的认识和了解。

整个功能的实现原理,概括如下:

  • Server 端作为整个的请求控制中心;
  • PC 端发起二维码登录请求,从 Server 端获取登录的二维码内容 A,并转换为二维码展示在页面;
  • App 端通过扫一扫获取到二维码内容 A,提交给 Server 端,并通知 Server 端对 A 完成登录或者取消登录操作;
  • PC 端通过 Server 拿到 App 端对 A 的操作处理结果,并做出相应的反应,最终完成登录;
  • 整个过程借助 Redis 存储内容 A 的状态,完成响应处理。

2. Server 端如何完成数据处理

如下接口是 PC 端向 Server 请求获取二维码内容时的接口代码:

   /**
     * <p>Description:登陆首页获取扫一扫登陆的二维码内容</p>
     * 1、二维码内容的有效时间是120S,生成的code是唯一码,存在redis缓存中,value里面带有该code的登陆状态
     * return:Map<String,Object>
     */
    @PostMapping(value="/getQrcodeContent")
    public ResponseBean getQrcodeContent(@RequestBody JSONObject jsonObject){
        String oldContext = jsonObject.containsKey("context")?jsonObject.getString("context"):"";
        //如果页面有旧的二维码,同时请求新的二维码内容,则直接删除旧内容
        if( !"".equals(oldContext) ){
            if( redisRepository.exists( Constants.QRCODE_LOGIN + oldContext.replace(Constants.QRCODE_HEADER, ""))){
Constants.QRCODE_LOGIN + oldContext.replace(Constants.QRCODE_HEADER, ""));
            }
        }

        String code = Base64.encode(UUID.randomUUID().toString());
        String context = Constants.QRCODE_HEADER + code;
        //将生成的code存入redis,失效时间为120S
        redisRepository.setExpire(Constants.QRCODE_LOGIN + code, QrCodeEnmu.logout.toString(), 120);
        return new ResponseBean(ResponseConstant.SUCCESS, context);
    }

可以看到,该接口中完成了如下的处理:

  • 生成一个唯一码 code,并存入 Redis,有效时间为 120S,Redis 的 key 为 Constants.QRCODE_LOGIN + code,Redis 的 value 为 logout;
  • 生成二维码内容,Constants.QRCODE_HEADER + code,并返回 PC 端;
  • 未过期二维码数据的处理。

3. PC 端是如何检测到 App 端已经完成了扫码操作

PC 端在拿到二维码内容之后,即可进行展示用于扫码的二维码;与此同时,PC 端会发起一个检查二维码状态变化的轮询请求,以保证实时检测到状态的变化;这里解释说明一下,因为我使用的是轮询的方式进行,而非 WebSocket 通信(全双工)。

为什么这么做,主要有以下几方面:

  • WebSocket 目前并不支持所有的浏览器,兼容性不好,而 HTTP 请求则不用考虑兼容性问题;
  • 对 HTTP 请求做了一定的优化处理,并不是每秒向服务器发送很多请求,而是在服务器端通过指定超时时间(10s)的策略不断检查 Redis 缓存中 key 对应的 value 的变化,以减少前后端请求的交互;
  • 很多大厂在处理同样的场景时,一样没有选择 WebS
作者正在撰写中...
隐藏内容 支付可见
内容互动
写评论
加载更多
评论文章
¥4.99 购买
× 订阅 Java 精选频道
¥ 元/月
订阅即可免费阅读所有精选内容