Java实现OTP(动态口令)服务
作者:nbsaas-boot
什么是 OTP?
OTP 的全称是 One-Time Password,中文常称为“一次性密码”或“动态口令”。它是一种动态生成的短时有效密码,用于身份验证,通常在登录或执行敏感操作时提供额外的安全保障。OTP 广泛应用于 Google、微软、GitHub 等主流平台,以增强用户账户的安全性。
OTP 的特点包括:
- 一次性使用:每个密码只能使用一次,无法重复。
- 时效性:密码在短时间内有效,过期后无法使用。
- 动态生成:密码基于时间或计数器动态生成。
OTP 的生成原理
常见的 OTP 实现标准有两种:
HOTP(HMAC-Based One-Time Password):基于计数器的 OTP。
TOTP(Time-Based One-Time Password):基于时间的 OTP。
TOTP 是目前使用最广泛的标准。它以共享密钥(secret key)和当前时间为输入,结合 HMAC-SHA1 算法生成短数字密码。以下是 TOTP 的主要步骤:
将当前时间戳除以时间步长(例如 30 秒)得到时间索引。
使用 HMAC 算法计算时间索引的哈希值。
提取哈希值的动态偏移量,并生成一个 6 位或 8 位的数字密码。
用 Java 实现 OTP 服务
以下是一个基于 Java 的 OTP 服务实现,支持生成和验证 OTP。
引入依赖
在项目的 pom.xml
文件中添加以下依赖:
<dependencies> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> </dependency> </dependencies>
实现 OTP 生成逻辑
以下代码实现了基于时间的 TOTP 生成和验证功能:
import java.nio.ByteBuffer; import java.time.Instant; import org.apache.commons.codec.binary.Base32; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class OtpService { private static final int TIME_STEP = 30; // 时间步长(秒) private static final int OTP_LENGTH = 6; // OTP 长度 private static final int MAX_ATTEMPTS = 5; // 最大尝试次数 private static final long BLOCK_DURATION = 300_000; // 封锁时间(毫秒) private final ConcurrentHashMap<String, AtomicInteger> attemptCounter = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Long> blockedUsers = new ConcurrentHashMap<>(); // 生成 TOTP public String generateTOTP(String secret) throws Exception { long timeIndex = Instant.now().getEpochSecond() / TIME_STEP; return generateOtp(secret, timeIndex); } // 验证 TOTP public boolean validateTOTP(String secret, String otp, String userId) throws Exception { if (isBlocked(userId)) { System.out.println("User is temporarily blocked: " + userId); return false; } long timeIndex = Instant.now().getEpochSecond() / TIME_STEP; // 在验证窗口内检查 OTP for (int i = -1; i <= 1; i++) { String generatedOtp = generateOtp(secret, timeIndex + i); if (generatedOtp.equals(otp)) { resetAttempts(userId); return true; } } recordFailedAttempt(userId); return false; } private String generateOtp(String secret, long timeIndex) throws Exception { // 解码 Base32 密钥 Base32 base32 = new Base32(); byte[] keyBytes = base32.decode(secret); // 转换时间索引为字节数组 byte[] timeBytes = ByteBuffer.allocate(8).putLong(timeIndex).array(); // 使用 HMAC-SHA1 生成哈希 Mac mac = Mac.getInstance("HmacSHA1"); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "HmacSHA1"); mac.init(keySpec); byte[] hash = mac.doFinal(timeBytes); // 提取动态偏移量 int offset = hash[hash.length - 1] & 0x0F; int binary = ((hash[offset] & 0x7F) << 24) | ((hash[offset + 1] & 0xFF) << 16) | ((hash[offset + 2] & 0xFF) << 8) | (hash[offset + 3] & 0xFF); // 生成 OTP int otp = binary % (int) Math.pow(10, OTP_LENGTH); return String.format("%0" + OTP_LENGTH + "d", otp); } private void recordFailedAttempt(String userId) { attemptCounter.putIfAbsent(userId, new AtomicInteger(0)); int attempts = attemptCounter.get(userId).incrementAndGet(); if (attempts >= MAX_ATTEMPTS) { blockedUsers.put(userId, System.currentTimeMillis()); System.out.println("User blocked due to multiple failed attempts: " + userId); } } private void resetAttempts(String userId) { attemptCounter.remove(userId); blockedUsers.remove(userId); } private boolean isBlocked(String userId) { Long blockTime = blockedUsers.get(userId); if (blockTime == null) { return false; } if (System.currentTimeMillis() - blockTime > BLOCK_DURATION) { blockedUsers.remove(userId); return false; } return true; } }
测试服务
以下是使用上述 OTP 服务生成和验证 OTP 的示例:
public class OtpServiceTest { public static void main(String[] args) throws Exception { OtpService otpService = new OtpService(); // 使用 Base32 编码密钥 String secret = "JBSWY3DPEHPK3PXP"; String userId = "user123"; // 生成 OTP String otp = otpService.generateTOTP(secret); System.out.println("Generated OTP: " + otp); // 尝试验证 OTP for (int i = 0; i < 7; i++) { boolean isValid = otpService.validateTOTP(secret, otp, userId); System.out.println("Attempt " + (i + 1) + ": Is OTP valid: " + isValid); } } }
优化建议
安全性:
使用安全随机数生成器生成共享密钥。
通过 HTTPS 传输数据,防止中间人攻击。
时间同步:
客户端和服务器之间的时间必须同步,否则 OTP 验证可能失败。
防止 暴 力 破 解:
增加失败尝试次数限制和封锁机制。
生产环境:
在数据库中安全存储共享密钥,避免泄露。
实现速率限制以防止暴 力 破 解攻击。
本文介绍了 OTP 的基本原理,并通过 Java 实现了一个简单的 OTP 服务,能够兼容 Google Authenticator 和 Microsoft Authenticator 等主流应用。OTP 技术通过动态密码的方式为用户提供了额外的身份验证安全保障,是目前最可靠的双因素认证技术之一。
到此这篇关于Java实现OTP(动态口令)服务的文章就介绍到这了,更多相关Java OTP动态口令内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!