Springboot+redis完成app扫码登录操作(轮询or长轮询)

Springboot+redis完成app扫码登录操作(轮询or长轮询)

  • 扫码登录流程
  • 集群中使用长轮询完成状态感知
  • demo完整代码地址:https://gitee.com/wyaoao/scan-code-login-demo
扫码登录实现大致流程
  1. web端发起请求生成二维码 ,服务记录生成二维码状态(等待扫码)及其唯一标识ID。

  2. web端渲染二维码、使用二维码标识ID发起轮询或者长轮询获取二维状态。

    • 轮询 : 实现简单不存在考虑集群问题。

    • 长轮询&长链接: 考虑到集群环境下实现会有繁琐的操作。

  3. app端对二维码进行扫码,改变二维码状态(扫码中)

  4. App在对扫码进行确认(已确认)或者取消操作(已取消)。

    • 确认的时候进行web新token生成,在长轮询返回给web端

请添加图片描述

主要麻烦一点是使用 Spring DeferredResult 在集群环境下怎样去找到,正在查询二维码状态的那个长链接。

LongPollFactory

public class LongPollFactory {

    private static final Map<String, DeferredResult<Result<QrCodeInfo>>> ONLINE_SESSION_CLIENT_MAP = new ConcurrentHashMap<>();

    public static void put(String qrcodeId, DeferredResult<Result<QrCodeInfo>> session) {
        ONLINE_SESSION_CLIENT_MAP.put(qrcodeId, session);
    }

    public static DeferredResult<Result<QrCodeInfo>> get(String qrcodeId) {
        return ONLINE_SESSION_CLIENT_MAP.get(qrcodeId);
    }

    public static void remove(String qrcodeId) {
        ONLINE_SESSION_CLIENT_MAP.remove(qrcodeId);
    }

}

Controller的示例:

    @GetMapping("/checkQrStateV2/{qrcodeId}")
    public DeferredResult<Result<QrCodeInfo>> checkQrStateV2(@PathVariable("qrcodeId") String qrcodeId) {
        //初始化DeferredResult 超时时间  和超时后返回什么内容
        final DeferredResult<Result<QrCodeInfo>> deferredResult = new DeferredResult<>(
                20000L,
                () -> Result.ok(qrcodeLoginService.checkQrState(qrcodeId))
        );
        //判断二维码不存在立即返回
        final boolean exists = qrcodeLoginService.getQrCodeInfoBucket(qrcodeId).isExists();
        if (!exists) {
            deferredResult.setResult(Result.ok(new QrCodeInfo(qrcodeId, QrCodeStatusEnum.INVALID)));
        }
        //超时 & 完成 时的操作
        deferredResult.onTimeout(() -> {
            log.info("QrcodeLoginController#checkQrStateV2 调用超时 qrcodeId:{}", qrcodeId);
            LongPollFactory.remove(qrcodeId);
        });
        deferredResult.onCompletion(() -> {
            log.info("QrcodeLoginController#checkQrStateV2 调用完成 qrcodeId:{}", qrcodeId);
            LongPollFactory.remove(qrcodeId);
        });
        //放入长轮询的工程,在状态变化是通知使用
        LongPollFactory.put(qrcodeId, deferredResult);
        return deferredResult;
    }

这里项目正好使用了Redis,这里就使用redis发布订阅完成消息通知。

消息监听:

@Slf4j
@Component
public class QrCodeStateChangeListener implements ApplicationRunner {

    private final RedissonClient redissonClient;

    private final QrcodeLoginService qrcodeLoginService;


    public QrCodeStateChangeListener(RedissonClient redissonClient,
                                     QrcodeLoginService qrcodeLoginService) {
        this.redissonClient = redissonClient;
        this.qrcodeLoginService = qrcodeLoginService;
    }

    @Override
    public void run(ApplicationArguments args) {
        final String topic = RedisGenerateKeyUtils.buildKey(TopicConstant.QRCODE_STATE_CHANGE_TOPIC);
        final RTopic rTopic = redissonClient.getTopic(topic, JsonJacksonCodec.INSTANCE);
        rTopic.addListener(String.class, (channel, qrcodeId) -> {
            log.info("topic: {} 收到消息 {}.", channel, qrcodeId);
            //能够在工程
            final DeferredResult<Result<QrCodeInfo>> deferredResult = LongPollFactory.get(qrcodeId);
            if (Objects.isNull(deferredResult)) {
                return;
            }
            deferredResult.setResult(Result.ok(qrcodeLoginService.checkQrState(qrcodeId)));
        });
    }

}

demo完整代码地址

https://gitee.com/wyaoao/scan-code-login-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值