扫码登录实现原理

Created at 2020-01-11 Updated at 2020-07-29 Category 项目

passport 中涉及的接口:

getQRCode(pc 端获得登录二维码)

  1. 生成一个随机字符串 code,调用 setex 命令,将这个 code 拼接前缀后作为 key 存入 redis 同时设置一个过期时间,过期后二维码失效需要刷新重新获取。
  2. 存储成功后返回 code。
public static function getQRCode() {
    $code = self::gen64Random();
    $redis = self::getRedis();
    $redis->setEx(self::makeQrCode($code), self::QR_CODE_VALID_TIME, 0);

    $ret = [
        'token' => $code,
    ];
    return Error::serviceRet(Error::ERR_NO_SUCCESS, $ret, '获取成功!');
}

appLogin(app 扫码)

  1. 手机扫码可以得到 code 和登录接口,app 调用 appLogin 接口传入用户 memberId 和 code。
  2. 服务器解析得到用户 memberId 和 code,用 code 去 redis 查询二维码是否过期。
  3. 如果没过期,将 code 作为键 memberID 作为值存入 redis,建立 memberId 与 code 的联系。
  4. 踢出其他登录信息。
$status = $redis->get(self::makeQrCode($code));
if($status === false) {
    return Error::serviceRet(1, '', '二维码已失效,请刷新重试!');
}

$redis->set(self::makeQrCode($code), 1);
$redis->setEx(self::makeQrToken($code), $validTime, $memberId)
// 踢出其他登录的信息
$loginToken = $redis->get(self::makeQrLoginTokenKey($memberId));
if($loginToken != null) {
    $redis->delete(self::makeQrLoginToken($loginToken));
}

getQRStatus(pc 轮询扫码状态)

  1. 浏览器拿到二维码之后,每隔一秒向浏览器发送一次登录是否成功的请求(调用接口 ‘getQRStatus’)。
  2. 请求中携带 code,先查询二维码是否失效,失效则返回二维码已失效。
  3. 然后用 code 查询 redis 是否有 memberId。
  4. 如果有 memberId 就登陆成功并设置对应的 loginToken,返回 loginToken,没有就继续轮询。
$redis = self::getRedis();
$status = $redis->get(self::makeQrCode($code));
if($status === false) {
    return Error::serviceRet(2, '', '二维码已失效!');
}

$memberId = $redis->get(self::makeQrToken($token));
if($memberId) { // 登陆成功
    $redis->del(self::makeQrToken($token));
    $redis->del(self::makeQrCode($token));
    $loginToken = self::gen64Random();

    // 设置对应的登陆token
    $redis->setEx(self::makeQrLoginToken($loginToken), $validTime, $memberId)
    // 保存用户的登录信息,以便下次用户扫描二维码时将该loginToken失效
    $redis->setEx(self::makeQrLoginTokenKey($memberId), $validTime, $loginToken)
    $ret = [
        'loginToken' => $loginToken,
        'status' => $status,
    ];
    return Error::serviceRet(Error::ERR_NO_SUCCESS, $ret, '登录成功!')
}

参考文章

Site by Cellophane using Hexo & Random

Hide