算法~totp用作签名防止url被复用

之前写过关于totp的文章,对它的基础有不清楚的同学,可以先看我的这篇文章《TOTP基础一》《TOTP基础二

想到的问题

因为totp是把时间分成了一个一个小的时间窗口,当生成totp的服务器和校验totp的服务器不在一起时间窗口,就会出现验证失败的问题,这是不可避免的,时间戳是一个long类型的数字,当这个数字进行totp运算后,落在哪个窗口里是确定的,但你的时间戳数字是不确定的,因为你不知道用户在什么时间触发totp这个生产的动作。

时间窗口

从下面的图中可以看到,时间两个窗口总会有一个交叉,谁赶上了都不一定,所以我们要解决这个不一定的问题。

最多跨一个窗口

我们的生成totp端和校验totp端,他们最多相差一个窗口,当然这也取决于,生产和校验的时间差和你的窗口大小,正常情况下相差1个窗口,所以我们比较上一个和下一个窗口即可。

/**
   * 生成一组TOTP,窗口大小5分钟,产生8位的动态码.
   * @param base32Key
   * @return
   */
  public static List<String> generateTOTPList(String base32Key) {
    long currentTime = System.currentTimeMillis();
    long prevTime = currentTime - 30 * 1000;
    long nextTime = currentTime + 30 * 1000;
    List<String> result = new ArrayList<>();
    result.add(generateTOTP(prevTime, base32Key, 300, 8));
    result.add(generateTOTP(currentTime, base32Key, 300, 8));
    result.add(generateTOTP(nextTime, base32Key, 300, 8));
    return result;
  }

我们在进行校验时,用这3个totp码与目标totp进行对比,有一个满足,就说明是合法的!即,我们的容错率是前后1个窗口。