Java實(shí)現(xiàn)OTP(動(dòng)態(tài)口令)服務(wù)
什么是 OTP?
OTP 的全稱是 One-Time Password,中文常稱為“一次性密碼”或“動(dòng)態(tài)口令”。它是一種動(dòng)態(tài)生成的短時(shí)有效密碼,用于身份驗(yàn)證,通常在登錄或執(zhí)行敏感操作時(shí)提供額外的安全保障。OTP 廣泛應(yīng)用于 Google、微軟、GitHub 等主流平臺(tái),以增強(qiáng)用戶賬戶的安全性。
OTP 的特點(diǎn)包括:
- 一次性使用:每個(gè)密碼只能使用一次,無法重復(fù)。
- 時(shí)效性:密碼在短時(shí)間內(nèi)有效,過期后無法使用。
- 動(dòng)態(tài)生成:密碼基于時(shí)間或計(jì)數(shù)器動(dòng)態(tài)生成。
OTP 的生成原理
常見的 OTP 實(shí)現(xiàn)標(biāo)準(zhǔn)有兩種:
HOTP(HMAC-Based One-Time Password):基于計(jì)數(shù)器的 OTP。
TOTP(Time-Based One-Time Password):基于時(shí)間的 OTP。
TOTP 是目前使用最廣泛的標(biāo)準(zhǔn)。它以共享密鑰(secret key)和當(dāng)前時(shí)間為輸入,結(jié)合 HMAC-SHA1 算法生成短數(shù)字密碼。以下是 TOTP 的主要步驟:
將當(dāng)前時(shí)間戳除以時(shí)間步長(例如 30 秒)得到時(shí)間索引。
使用 HMAC 算法計(jì)算時(shí)間索引的哈希值。
提取哈希值的動(dòng)態(tài)偏移量,并生成一個(gè) 6 位或 8 位的數(shù)字密碼。
用 Java 實(shí)現(xiàn) OTP 服務(wù)
以下是一個(gè)基于 Java 的 OTP 服務(wù)實(shí)現(xiàn),支持生成和驗(yàn)證 OTP。
引入依賴
在項(xiàng)目的 pom.xml 文件中添加以下依賴:
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
</dependencies>實(shí)現(xiàn) OTP 生成邏輯
以下代碼實(shí)現(xiàn)了基于時(shí)間的 TOTP 生成和驗(yàn)證功能:
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; // 時(shí)間步長(秒)
private static final int OTP_LENGTH = 6; // OTP 長度
private static final int MAX_ATTEMPTS = 5; // 最大嘗試次數(shù)
private static final long BLOCK_DURATION = 300_000; // 封鎖時(shí)間(毫秒)
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);
}
// 驗(yàn)證 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;
// 在驗(yàn)證窗口內(nèi)檢查 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);
// 轉(zhuǎn)換時(shí)間索引為字節(jié)數(shù)組
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);
// 提取動(dòng)態(tài)偏移量
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;
}
}測試服務(wù)
以下是使用上述 OTP 服務(wù)生成和驗(yàn)證 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);
// 嘗試驗(yàn)證 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);
}
}
}優(yōu)化建議
安全性:
使用安全隨機(jī)數(shù)生成器生成共享密鑰。
通過 HTTPS 傳輸數(shù)據(jù),防止中間人攻擊。
時(shí)間同步:
客戶端和服務(wù)器之間的時(shí)間必須同步,否則 OTP 驗(yàn)證可能失敗。
防止 暴 力 破 解:
增加失敗嘗試次數(shù)限制和封鎖機(jī)制。
生產(chǎn)環(huán)境:
在數(shù)據(jù)庫中安全存儲(chǔ)共享密鑰,避免泄露。
實(shí)現(xiàn)速率限制以防止暴 力 破 解攻擊。
本文介紹了 OTP 的基本原理,并通過 Java 實(shí)現(xiàn)了一個(gè)簡單的 OTP 服務(wù),能夠兼容 Google Authenticator 和 Microsoft Authenticator 等主流應(yīng)用。OTP 技術(shù)通過動(dòng)態(tài)密碼的方式為用戶提供了額外的身份驗(yàn)證安全保障,是目前最可靠的雙因素認(rèn)證技術(shù)之一。
到此這篇關(guān)于Java實(shí)現(xiàn)OTP(動(dòng)態(tài)口令)服務(wù)的文章就介紹到這了,更多相關(guān)Java OTP動(dòng)態(tài)口令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁示例
本文介紹了SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
Java中String的JdbcTemplate連接SQLServer數(shù)據(jù)庫的方法
這篇文章主要介紹了Java中String的JdbcTemplate連接SQLServer數(shù)據(jù)庫的方法,在研發(fā)過程中我們需要與其他系統(tǒng)對(duì)接的場景,連接SQLServer拉取數(shù)據(jù),所以就用jdbc連接數(shù)據(jù)庫的方式連接外部數(shù)據(jù)源,需要的朋友可以參考下2021-10-10
SpringBoot頂層接口實(shí)現(xiàn)類注入項(xiàng)目的方法示例
本文主要介紹了SpringBoot頂層接口實(shí)現(xiàn)類注入項(xiàng)目的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
Java運(yùn)行時(shí)動(dòng)態(tài)生成類實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Java運(yùn)行時(shí)動(dòng)態(tài)生成類實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Springboot如何操作redis數(shù)據(jù)
這篇文章主要介紹了Springboot如何操作redis數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Spring Boot學(xué)習(xí)入門之表單驗(yàn)證
表單驗(yàn)證主要是用來防范小白搞亂網(wǎng)站和一些低級(jí)的黑客技術(shù)。Spring Boot可以使用注解 @Valid 進(jìn)行表單驗(yàn)證。下面這篇文章主要給大家介紹了關(guān)于Spring Boot學(xué)習(xí)入門之表單驗(yàn)證的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09
Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)條件與范圍查詢實(shí)例代碼
本篇文章主要介紹了Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)條件與范圍查詢實(shí)例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-06-06
在IDEA中搭建最小可用SpringMVC項(xiàng)目(純Java配置)
這篇文章主要介紹了在IDEA中搭建最小可用SpringMVC項(xiàng)目(純Java配置),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Java編程中使用JDBC API連接數(shù)據(jù)庫和創(chuàng)建程序的方法
這篇文章主要介紹了Java編程中使用JDBC API連接數(shù)據(jù)庫和創(chuàng)建程序的基本教程,JDBC是一種用于執(zhí)行SQL語句的Java API,可以為多種關(guān)系數(shù)據(jù)庫提供統(tǒng)一訪問需要的朋友可以參考下2015-12-12

