Spring Boot-扫码登录轮询版

228 阅读3分钟

扫码登录轮询版

访问Gitee仓库-Guet Demo

目录

扫码登录时序图

Untitled.jpeg

扫码登录轮询版代码实现

  • PC端生成二维码—需要注意的是要对缓存在Redis中的二维码设置过期时间否则用户不进行扫码,二维码将会在Redis缓存中累积。
/**
     * 根据 uuid生成二维码
     * @return
     */
    @Override
    public ResponseEntity<byte[]> generateQRUUID() throws IOException, WriterException {
        //生成uuid
        String uuid = UUID.randomUUID().toString();
        System.out.println(uuid);
        //将标识符存储到redis,设置两分钟过期
        redisCache.setCacheObject(uuid,"false",120, SECONDS);
        //生成二维码图片
        int width = 200;
        int height = 200;
        String format = "png";
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET,"UTF-8");
        BitMatrix bitMatrix = new MultiFormatWriter().encode(uuid, BarcodeFormat.QR_CODE,width,height,hints);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix,format,out);
        byte[] bytes = out.toByteArray();
        //返回二维码
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        return new ResponseEntity<>(bytes,headers, HttpStatus.OK);
    }
  • 手机端扫描二维码—事实上手机端扫描之后可以将手机端登录用户的jwt与二维码状态一同存入Redis缓存,等待PC端轮询时即可从Redis中取出jwt解析用户信息,用于显示确认界面。
/**
     * 扫描二维码二维码状态改变
     * @param httpServletRequest
     * @param httpServletResponse
     * @return
     */
    @Override
    public ResponseResult scanQRCode(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        //从请求头中获取二维码的uuid
        String qrUUID = httpServletRequest.getHeader("qrcode-uuid");

        //从redis中取出标识符,将二维码状态修改为待确认,表示扫码成功
        String value = redisCache.getCacheObject(qrUUID);
        if (value == null) {
            WebUtils.setStatus(httpServletResponse,AppHttpCodeEnum.QRCODE_EXPIRE.getCode());
            return ResponseResult.errorResult(AppHttpCodeEnum.QRCODE_EXPIRE);
        }
        //二维码不能重复扫描,不过第一次扫描的时候已经将扫描二维码的登录的用户的信息返回给前端了,前端会切换扫描界面为确认界面,应该也不会出现重复扫描的情况
        if (value.equals("toBeConfirmed")) {
            WebUtils.setStatus(httpServletResponse,400);
            return ResponseResult.errorResult(400,"此二维码已经被扫描");
        }

        //拿到扫码用户的token,解析用户信息
        String phoneUserJwt = httpServletRequest.getHeader("token");
        //解析获取userid
        Claims claims = null;
        try {
            claims = jwtUtil.parseJWT(phoneUserJwt);
        } catch (Exception e) {
            e.printStackTrace();
            //token无法解析 token超时  token非法
        }
        String phoneUserId = claims.getSubject();
        //从redis中获取用户信息
        UserLoginVo phoneUserLoginVo = redisCache.getCacheObject("guetUser" + phoneUserId);

        //扫码成功后提供用户一分钟确认时间
        redisCache.setCacheObject(qrUUID,"toBeConfirmed",60, SECONDS);
        return ResponseResult.okResult(phoneUserLoginVo);
    }
  • 手机端确认登录
/**
     * 确认二维码登录
     * @param httpServletRequest
     * @param httpServletResponse
     * @return
     */
    @Override
    public ResponseResult confrimQRLogin(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        //拿到二维码标识
        String uuid = httpServletRequest.getHeader("qrcode-uuid");
        //拿到扫码用户的jwt
        String jwt = httpServletRequest.getHeader("token");
        //从redis中取出标识符,并将其值修改为认证的用户信息,表示扫码成功
        String value = redisCache.getCacheObject(uuid);
        if (value == null) {
            WebUtils.setStatus(httpServletResponse,AppHttpCodeEnum.QRCODE_EXPIRE.getCode());
            return ResponseResult.errorResult(AppHttpCodeEnum.QRCODE_EXPIRE);
        }
        //二维码如果没被扫描(是否多余呢,如果用户不扫描就不可能有正确的qrUUID)
        if (value.equals("false")) {
            WebUtils.setStatus(httpServletResponse,400);
            return ResponseResult.errorResult(400,"此二维码还未被扫描无法确认");
        }
        //确认成功
        redisCache.setCacheObject(uuid,jwt);
        return ResponseResult.okResult("用户确认登录,等待前端轮询");
    }
  • PC端轮询
/**
     * PC端轮询二维码状态
     * @param httpSession
     * @param httpServletRequest
     * @param httpServletResponse
     * @return
     */
    @Override
    public ResponseResult checkQRCode(HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        String uuid = httpServletRequest.getHeader("qrcode-uuid");
        String value = redisCache.getCacheObject(uuid);
        if (value == null) {
            WebUtils.setStatus(httpServletResponse,AppHttpCodeEnum.QRCODE_EXPIRE.getCode());
            return ResponseResult.errorResult(AppHttpCodeEnum.QRCODE_EXPIRE);
        }

        if(value.equals("false")) {
            WebUtils.setStatus(httpServletResponse,400);
            return ResponseResult.errorResult(400,"没有用户扫码二维码");
        }

        if (value.equals("toBeConfirmed")) {
            WebUtils.setStatus(httpServletResponse,400);
            return ResponseResult.errorResult(400,"用户扫码了二维码,待确认登录");
        }

        //以下说明用户确认进行登录
        //获取授权用户的登录信息
        String phoneUserJwt = redisCache.getCacheObject(uuid);
        //解析获取userid
        Claims claims = null;
        try {
            claims = jwtUtil.parseJWT(phoneUserJwt);
        } catch (Exception e) {
            e.printStackTrace();
            //token无法解析 token超时  token非法
        }
        String phoneUserId = claims.getSubject();
        //从redis中获取用户信息
        UserLoginVo phoneUserLoginVo = redisCache.getCacheObject("guetUser" + phoneUserId);

        //构建pc用户登录
        String userAgent = phoneUserLoginVo.getUserAgent();
        String loginType2 = "scanCode";
        UserVo userVo = phoneUserLoginVo.getUserVo();
        String jwt = JwtUtil.createJWT(httpSession.getId(),JwtUtil.JWT_TTL);
        UserLoginVo userLoginVo = new UserLoginVo(jwt,userAgent,loginType2,userVo);
        //session存储用户信息
        httpSession.setAttribute("User",userLoginVo);
        //存入redis中
        redisCache.setCacheObject("guetUser"+httpSession.getId(),userLoginVo);
        redisCache.expire("guetUser"+httpSession.getId(),60*60);
        System.out.println(httpSession.getAttribute("User")+"会话中的用户信息");

        //删除二维码
        redisCache.deleteObject(uuid);
        return ResponseResult.okResult(userLoginVo);
    }

扫码登录测试流程

  • 浏览器请求生成二维码:

Untitled.png

Untitled 1.png

  • PC端轮询1:

Untitled 2.png

  • 手机用户扫描二维码:

Untitled 3.png

  • PC端轮询2:

Untitled 4.png

  • 手机端用户确认登录:

Untitled 5.png

  • PC端轮询3:

Untitled 6.png