一次性验证码的简单使用
前言
一次性验证码(One-Time Password, OTP)是一种动态生成且仅能使用一次的密码,主要用于增强账户安全性,防止密码泄露或重放攻击。
使用
添加依赖
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>java-otp</artifactId>
<version>0.2.0</version>
</dependency>
代码实现
package com.imooc;
import com.eatthepath.otp.HmacOneTimePasswordGenerator;
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
/**
* 一次性验证码
*/
public class TestOtp2 {
private static TimeBasedOneTimePasswordGenerator totp;
static {
try {
totp = new TimeBasedOneTimePasswordGenerator();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
//同一个输入在同一个时间窗口内验证码是一样的,如果想要不一样,可以拼接上时间戳或计数器等变量
String input = "123456";
String otpCode = generateOtpCode(input, Instant.now());
System.out.println("otpCode:" + otpCode);
TimeUnit.SECONDS.sleep(31);
System.out.println(verifyOtpCode(input, otpCode, 0));
}
/**
* 生成验证码
*
* @param input 输入数据
* @param timestamp 时间戳
* @return
*/
private static String generateOtpCode(String input, Instant timestamp) throws Exception {
byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
SecretKeySpec keySpec = new SecretKeySpec(bytes, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);
int code = totp.generateOneTimePassword(keySpec, timestamp);
// 位数不足左补0
int len = HmacOneTimePasswordGenerator.DEFAULT_PASSWORD_LENGTH;
return String.format("%0" + len + "d", code);
}
/**
* 校验验证码是否正确,比如容忍度是1,那就是校验90秒内这个验证码是否是有效的
*
* @param input 之前的输入数据
* @param otpCode 验证码
* @param tolerance 容忍度
* @return
*/
private static boolean verifyOtpCode(String input, String otpCode, int tolerance) throws Exception {
byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
SecretKeySpec keySpec = new SecretKeySpec(bytes, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);
Instant now = Instant.now();
// 当前时间戳/30000
long counter = now.toEpochMilli() / totp.getTimeStep().toMillis();
// 30秒一个时间窗口
for (long ts = counter - tolerance; ts <= counter + tolerance; ts++) {
int code = totp.generateOneTimePassword(keySpec, ts);
// 位数不足左补0
int len = HmacOneTimePasswordGenerator.DEFAULT_PASSWORD_LENGTH;
String newOtpCode = String.format("%0" + len + "d", code);
if (otpCode.equals(newOtpCode)) {
return true;
}
}
return false;
}
}

浙公网安备 33010602011771号