算法~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个窗口。