欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實(shí)現(xiàn)OTP(動態(tài)口令)服務(wù)

 更新時間:2025年03月10日 11:00:22   作者:nbsaas-boot  
OTP是一種動態(tài)生成的短時有效密碼,用于身份驗(yàn)證,通常在登錄或執(zhí)行敏感操作時提供額外的安全保障,本文主要介紹了Java實(shí)現(xiàn)OTP(動態(tài)口令)服務(wù),感興趣的可以了解一下

什么是 OTP?

OTP 的全稱是 One-Time Password,中文常稱為“一次性密碼”或“動態(tài)口令”。它是一種動態(tài)生成的短時有效密碼,用于身份驗(yàn)證,通常在登錄或執(zhí)行敏感操作時提供額外的安全保障。OTP 廣泛應(yīng)用于 Google、微軟、GitHub 等主流平臺,以增強(qiáng)用戶賬戶的安全性。

OTP 的特點(diǎn)包括:

  • 一次性使用:每個密碼只能使用一次,無法重復(fù)。
  • 時效性:密碼在短時間內(nèi)有效,過期后無法使用。
  • 動態(tài)生成:密碼基于時間或計數(shù)器動態(tài)生成。

OTP 的生成原理

常見的 OTP 實(shí)現(xiàn)標(biāo)準(zhǔn)有兩種:

  • HOTP(HMAC-Based One-Time Password):基于計數(shù)器的 OTP。

  • TOTP(Time-Based One-Time Password):基于時間的 OTP。

TOTP 是目前使用最廣泛的標(biāo)準(zhǔn)。它以共享密鑰(secret key)和當(dāng)前時間為輸入,結(jié)合 HMAC-SHA1 算法生成短數(shù)字密碼。以下是 TOTP 的主要步驟:

  • 將當(dāng)前時間戳除以時間步長(例如 30 秒)得到時間索引。

  • 使用 HMAC 算法計算時間索引的哈希值。

  • 提取哈希值的動態(tài)偏移量,并生成一個 6 位或 8 位的數(shù)字密碼。

用 Java 實(shí)現(xiàn) OTP 服務(wù)

以下是一個基于 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)了基于時間的 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; // 時間步長(秒)
    private static final int OTP_LENGTH = 6; // OTP 長度
    private static final int MAX_ATTEMPTS = 5; // 最大嘗試次數(shù)
    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);
    }

    // 驗(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)換時間索引為字節(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);

        // 提取動態(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ù),防止中間人攻擊。

時間同步

  • 客戶端和服務(wù)器之間的時間必須同步,否則 OTP 驗(yàn)證可能失敗。

防止 暴 力 破 解

  • 增加失敗嘗試次數(shù)限制和封鎖機(jī)制。

生產(chǎn)環(huán)境

  • 在數(shù)據(jù)庫中安全存儲共享密鑰,避免泄露。

  • 實(shí)現(xiàn)速率限制以防止暴 力 破 解攻擊。

本文介紹了 OTP 的基本原理,并通過 Java 實(shí)現(xiàn)了一個簡單的 OTP 服務(wù),能夠兼容 Google Authenticator 和 Microsoft Authenticator 等主流應(yīng)用。OTP 技術(shù)通過動態(tài)密碼的方式為用戶提供了額外的身份驗(yàn)證安全保障,是目前最可靠的雙因素認(rèn)證技術(shù)之一。

到此這篇關(guān)于Java實(shí)現(xiàn)OTP(動態(tài)口令)服務(wù)的文章就介紹到這了,更多相關(guān)Java OTP動態(tài)口令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中字符串與日期的轉(zhuǎn)換實(shí)例

    java中字符串與日期的轉(zhuǎn)換實(shí)例

    java中字符串與日期的轉(zhuǎn)換實(shí)例,需要的朋友可以參考一下
    2013-05-05
  • C++內(nèi)存管理看這一篇就夠了

    C++內(nèi)存管理看這一篇就夠了

    這篇文章主要介紹了C/C++中的內(nèi)存管理小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • 深入了解MyBatis參數(shù)

    深入了解MyBatis參數(shù)

    今天小編就為大家分享一篇關(guān)于深入了解MyBatis參數(shù),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 詳解Java字節(jié)碼編程之非常好用的javassist

    詳解Java字節(jié)碼編程之非常好用的javassist

    這篇文章主要介紹了詳解Java字節(jié)碼編程之非常好用的javassist,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 基于restTemplate遇到的編碼問題及解決

    基于restTemplate遇到的編碼問題及解決

    這篇文章主要介紹了restTemplate遇到的編碼問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 詳解Spring Boot 目錄文件結(jié)構(gòu)

    詳解Spring Boot 目錄文件結(jié)構(gòu)

    這篇文章主要介紹了Spring Boot 目錄文件結(jié)構(gòu)的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • 如何將默認(rèn)的maven倉庫改為阿里的maven倉庫

    如何將默認(rèn)的maven倉庫改為阿里的maven倉庫

    這篇文章主要介紹了如何將默認(rèn)的maven倉庫改為阿里的maven倉庫,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • elasticsearch元數(shù)據(jù)構(gòu)建metadata及routing類源碼分析

    elasticsearch元數(shù)據(jù)構(gòu)建metadata及routing類源碼分析

    這篇文章主要為大家介紹了elasticsearch元數(shù)據(jù)構(gòu)建metadata?routing類內(nèi)部源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • MybatisPlus使用排序查詢時將null值放到最后

    MybatisPlus使用排序查詢時將null值放到最后

    按照更新時間排序,但是更新時間可能為null,因此將null的數(shù)據(jù)放到最后,本文主要介紹了MybatisPlus使用排序查詢時將null值放到最后,具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • 淺談java Collection中的排序問題

    淺談java Collection中的排序問題

    下面小編就為大家?guī)硪黄獪\談java Collection中的排序問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12

最新評論