一次性验证码的简单使用

前言

一次性验证码(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;
    }
}

posted @ 2025-04-06 11:20  strongmore  阅读(101)  评论(0)    收藏  举报