最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot實現(xiàn)JWT動態(tài)密鑰輪換的示例詳解

 更新時間:2025年10月15日 08:22:05   作者:風(fēng)象南  
這篇文章主要為大家詳細介紹了SpringBoot實現(xiàn)JWT動態(tài)密鑰輪換的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

背景:為什么 JWT 密鑰也要"輪換"

JWT(JSON Web Token) 是當(dāng)代認證體系的常用方案, 無論是單體系統(tǒng)、微服務(wù)、還是前后端分離登錄,幾乎都會用到它。

但在大多數(shù)系統(tǒng)里,簽名密鑰往往是一成不變的—— 一旦生成,常年不換,代碼里寫死或放在配置文件中。

這其實非常危險:

  • 一旦密鑰被誤傳或泄露,攻擊者就能偽造任意用戶的合法 Token
  • 無論是測試環(huán)境誤配置,還是日志誤打出 key,都可能導(dǎo)致密鑰泄露,帶來安全隱患

于是我們面臨一個工程問題:

"如何能動態(tài)更新 JWT 簽名密鑰,且不讓用戶重新登錄?"

目標(biāo):密鑰可定期更新,但不影響登錄狀態(tài)

我們的目標(biāo)是實現(xiàn):

時間點動作用戶狀態(tài)
10月1日使用 keypair_A 生成 JWT正常
10月10日上線 keypair_B,新簽發(fā)用它老 Token 仍有效
10月20日老 Token 全部過期刪除 keypair_A
  • 老 Token 正??沈灪?/li>
  • 新 Token 自動使用新密鑰
  • 用戶無感知,不掉線

簽名實現(xiàn):HMAC vs RSA

JWT 支持多種簽名算法,常見的有兩種:

類型算法示例是否對稱特點
HMAC(對稱)HS256 / HS512? 是簽發(fā)方與驗證方共用同一密鑰
RSA / ECDSA(非對稱)RS256 / ES256? 否簽發(fā)方用私鑰簽名,驗證方用公鑰驗簽

很多系統(tǒng)為了圖省事,默認使用 HMAC(例如 HS256)。 它確實簡單,但存在一個致命問題:

一旦 HMAC 密鑰泄露,攻擊者可以偽造任何合法 Token。

這意味著:

簽發(fā)方 = 驗證方 = 攻擊方(如果密鑰泄露)

沒有信任隔離

無法安全輪換:新舊密鑰都得讓驗證邏輯同時持有

這也是為什么更高安全等級的系統(tǒng)都改用 RSA / ECDSA 非對稱簽名。

安全輪換的關(guān)鍵:KID(Key ID)+ 多版本密鑰倉庫

JWT Header 允許帶一個 "kid" 字段,用來標(biāo)識當(dāng)前簽名使用的密鑰版本。 比如:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-20251013-956"
}

這樣,驗證方只需要:

  • 讀取 header.kid
  • 去 KeyStore 找對應(yīng)公鑰
  • 使用它來驗簽

老 Token 用老公鑰,新 Token 用新公鑰,完美共存。

核心實現(xiàn)

技術(shù)架構(gòu)

后端技術(shù)棧:

  • Spring Boot 3 + Spring Scheduling
  • JJWT 0.12.3(JWT 處理庫)
  • RSA 2048 非對稱加密
  • 內(nèi)存 ConcurrentHashMap 存儲(方便快速體驗DEMO)

前端技術(shù)棧:

  • HTML5 + CSS3 + JavaScript ES6
  • Tailwind CSS UI 框架
  • 前后端分離

核心組件設(shè)計

1.DynamicKeyStore - 動態(tài)密鑰存儲管理器

@Service
public class DynamicKeyStore {
    // 線程安全的密鑰存儲
    private final Map<String, KeyInfo> keyStore = new ConcurrentHashMap<>();
    private volatile String currentKeyId;

    // 生成新密鑰對
    public String generateNewKeyPair() {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048, new SecureRandom());
        KeyPair keyPair = generator.generateKeyPair();

        String keyId = "key-" + LocalDate.now() + "-" + timestamp;
        KeyInfo keyInfo = new KeyInfo(keyId, keyPair);

        // 輪換邏輯:舊密鑰標(biāo)記為非活躍,新密鑰設(shè)為當(dāng)前
        if (currentKeyId != null) {
            keyStore.get(currentKeyId).setActive(false);
        }
        currentKeyId = keyId;
        keyStore.put(keyId, keyInfo);

        return keyId;
    }

    // 根據(jù)KID獲取密鑰(支持多版本共存)
    public KeyInfo getKey(String keyId) {
        return keyStore.get(keyId);
    }
}

2.JwtTokenService - JWT 服務(wù)層

Token 生成(使用當(dāng)前活躍密鑰):

public String generateToken(String username, Map<String, Object> claims) {
    // 獲取當(dāng)前活躍密鑰
    var currentKey = keyStore.getCurrentKey();
    String keyId = currentKey.getKeyId();

    // 構(gòu)建JWT,設(shè)置KID
    JwtBuilder builder = Jwts.builder()
            .subject(username)
            .issuedAt(new Date())
            .expiration(Date.from(Instant.now().plus(24, ChronoUnit.HOURS)))
            .header().keyId(keyId).and()
            .signWith(currentKey.getKeyPair().getPrivate(), Jwts.SIG.RS256);

    // 添加自定義聲明
    if (claims != null && !claims.isEmpty()) {
        builder.claims().add(claims);
    }

    return builder.compact();
}

Token 驗證(支持多版本密鑰):

public Claims validateToken(String token) throws JwtException {
    // 1. 解析Header獲取KID
    String[] parts = token.split("\\.");
    String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]));
    Map<String, Object> headerMap = mapper.readValue(headerJson, Map.class);
    String keyId = (String) headerMap.get("kid");

    if (keyId == null) {
        throw new JwtException("Token缺少密鑰ID (kid)");
    }

    // 2. 根據(jù)KID獲取對應(yīng)公鑰
    var keyInfo = keyStore.getKey(keyId);
    if (keyInfo == null) {
        throw new JwtException("找不到對應(yīng)的密鑰: " + keyId);
    }
    PublicKey publicKey = keyInfo.getKeyPair().getPublic();

    // 3. 使用公鑰驗證Token
    Jws<Claims> jws = Jwts.parser()
            .verifyWith(publicKey)
            .build()
            .parseSignedClaims(token);

    return jws.getPayload();
}

3.KeyRotationScheduler - 定時輪換調(diào)度器

@Component
public class KeyRotationScheduler {

    @Value("${jwt.rotation-period-days:7}")
    private int rotationPeriodDays;

    @Value("${jwt.grace-period-days:14}")
    private int gracePeriodDays;

    // 應(yīng)用啟動時初始化
    @EventListener(ApplicationReadyEvent.class)
    public void initialize() {
        keyStore.initialize();
    }

    // 定時輪換:每天凌晨2點檢查
    @Scheduled(cron = "0 0 2 * * ?")
    public void scheduledKeyRotation() {
        var currentKey = keyStore.getCurrentKey();
        long daysSinceCreation = ChronoUnit.DAYS.between(
            currentKey.getCreatedAt(), LocalDateTime.now()
        );

        if (daysSinceCreation >= rotationPeriodDays) {
            String newKeyId = keyStore.generateNewKeyPair();
            logger.info("密鑰輪換完成: {} -> {}", currentKeyId, newKeyId);
        }
    }

    // 定時清理:每天凌晨3點清理過期密鑰
    @Scheduled(cron = "0 0 3 * * ?")
    public void scheduledKeyCleanup() {
        List<String> removedKeys = keyStore.cleanupExpiredKeys(gracePeriodDays);
        if (!removedKeys.isEmpty()) {
            logger.info("清理了 {} 個過期密鑰", removedKeys.size());
        }
    }
}

4.API接口

認證相關(guān):

  • POST /api/auth/login - 用戶登錄
  • POST /api/auth/validate - Token驗證
  • POST /api/auth/refresh - Token刷新
  • GET /api/auth/me - 獲取當(dāng)前用戶信息

管理功能:

  • POST /api/auth/admin/rotate-keys - 手動輪換密鑰
  • POST /api/auth/admin/cleanup-keys - 清理過期密鑰

演示功能:

  • GET /api/demo/key-stats - 獲取密鑰統(tǒng)計
  • POST /api/demo/parse-token - 解析Token
  • POST /api/demo/generate-test-token - 生成測試Token
  • GET /api/demo/protected - 受保護資源

5.前端交互界面

DEMO提供了完整的前后端分離演示界面

用戶登錄:登錄認證和狀態(tài)顯示

受保護資源:演示Token保護機制

密鑰信息:實時密鑰存儲狀態(tài)監(jiān)控

Token解析:JWT結(jié)構(gòu)分析工具

管理功能:手動密鑰輪換和清理

平滑過渡策略

密鑰輪換不是"替換",而是"共存"。

階段動作狀態(tài)
① 新密鑰上線新 Token 用新 Key 簽發(fā)雙密鑰并行
② 老 Token 仍驗證通過舊 Key 在驗證端保留用戶無感
③ 老 Token 過期刪除舊 Key安全收尾

整個過程無須人工干預(yù),也不需要讓用戶重新登錄。

關(guān)鍵驗證點

  • 新Token使用新密鑰:輪換后新生成的Token包含新的KID
  • 舊Token仍可驗證:輪換前的Token繼續(xù)正常使用
  • 用戶無感知:整個輪換過程對用戶完全透明
  • 系統(tǒng)監(jiān)控:實時查看密鑰狀態(tài)和輪換歷史

總結(jié)

在實際項目中,密鑰管理往往是被忽視的角落。直到安全審計時才發(fā)現(xiàn)問題。通過合理運用JWT的KID字段和RSA的非對稱特性,我們可以讓系統(tǒng)自動處理密鑰輪換,而不是事后補救。

從代碼量來看,增加密鑰輪換功能并不需要大幅改動現(xiàn)有架構(gòu),但帶來的安全收益是長期的。

到此這篇關(guān)于SpringBoot實現(xiàn)JWT動態(tài)密鑰輪換的示例詳解的文章就介紹到這了,更多相關(guān)SpringBoot JWT動態(tài)密鑰輪換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實現(xiàn)優(yōu)先隊列式廣度優(yōu)先搜索算法的示例代碼

    Java實現(xiàn)優(yōu)先隊列式廣度優(yōu)先搜索算法的示例代碼

    這篇文章主要為大家詳細介紹了Java如何實現(xiàn)優(yōu)先隊列式廣度優(yōu)先搜索算法,文中通過一個示例帶大家具體了解了實現(xiàn)的方法,需要的可以參考一下
    2022-08-08
  • 淺談spring boot使用thymeleaf版本的問題

    淺談spring boot使用thymeleaf版本的問題

    這篇文章主要介紹了spring boot使用thymeleaf版本的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 解決SpringBoot多模塊發(fā)布時99%的問題

    解決SpringBoot多模塊發(fā)布時99%的問題

    本文歸納了以下 8 個原則和發(fā)布時經(jīng)常出現(xiàn)的 4 個問題的解決方案,掌握了這些原則和解決方案,幾乎可以解決絕大數(shù)SpringBoot發(fā)布問題
    2019-07-07
  • Maven本地打包war包實現(xiàn)代碼解析

    Maven本地打包war包實現(xiàn)代碼解析

    這篇文章主要介紹了Maven本地打包war包實現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • 詳細聊聊RabbitMQ竟無法反序列化List問題

    詳細聊聊RabbitMQ竟無法反序列化List問題

    這篇文章主要給大家介紹了關(guān)于RabbitMQ竟無法反序列化List的相關(guān)資料,文中通過示例代碼將問題以及解決的過程介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2021-09-09
  • Spring MVC 圖片的上傳和下載功能

    Spring MVC 圖片的上傳和下載功能

    SSM 框架是一種基于Java的Web開發(fā)框架,其中Spring作為控制層、SpringMVC作為視圖層、MyBatis作為持久層,這個框架非常適合Web應(yīng)用程序的開發(fā),這篇文章主要介紹了Spring MVC 圖片的上傳和下載功能,需要的朋友可以參考下
    2023-03-03
  • java swing實現(xiàn)電影購票系統(tǒng)

    java swing實現(xiàn)電影購票系統(tǒng)

    這篇文章主要為大家詳細介紹了java swing實現(xiàn)電影購票系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Springmvc ajax跨域請求處理方法實例詳解

    Springmvc ajax跨域請求處理方法實例詳解

    這篇文章主要介紹了Springmvc ajax跨域請求處理方法實例詳解,需要的朋友可以參考下
    2017-10-10
  • Java+MySQL實現(xiàn)設(shè)計優(yōu)惠券系統(tǒng)

    Java+MySQL實現(xiàn)設(shè)計優(yōu)惠券系統(tǒng)

    這篇文章主要介紹了Java+MySQL實現(xiàn)設(shè)計優(yōu)惠券系統(tǒng),文章基于Java與MySQL的相關(guān)資料展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • 關(guān)于Lists.partition集合分組使用以及注意事項

    關(guān)于Lists.partition集合分組使用以及注意事項

    這篇文章主要介紹了關(guān)于Lists.partition集合分組使用以及注意事項,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評論

陆良县| 广安市| 山西省| 邢台县| 达孜县| 淮滨县| 五峰| 南平市| 黄龙县| 雅安市| 钦州市| 同江市| 石河子市| 宜州市| 南江县| 青州市| 蛟河市| 多伦县| 南汇区| 大丰市| 霍山县| 安国市| 竹北市| 历史| 射洪县| 靖江市| 册亨县| 天长市| 西盟| 吕梁市| 定襄县| 满洲里市| 油尖旺区| 富民县| 贵阳市| 大连市| 望奎县| 澜沧| 乡宁县| 松滋市| 东乡族自治县|