Spring Cloud項目登錄認證從JWT切換到Redis + UUID Token方案

背景介紹

在傳統的Spring Boot項目中,用戶登錄認證常見的方案是使用JWT(JSON Web Token)來實現無狀態的身份驗證。JWT憑借自包含用戶信息、方便前后端分離、性能較好等優勢被廣泛采用。

然而,在實際項目中,JWT也有一定缺點,比如:

  • 不能主動失效(除非設計復雜的黑名單機制)

  • token刷新邏輯復雜

  • 服務器無法靈活控制單點登出

為了提升認證的靈活性和安全性,我將項目的登錄鑒權方案由JWT切換成了 Redis + UUID Token 的模式,實現了服務端存儲與校驗,且支持token的自動續期。


為什么切換?

  • 支持主動注銷:退出登錄時,服務器可以直接刪除Redis中對應的Token。

  • 便于統一管理Token生命周期:可以靈活設置過期時間和續期策略。

  • 方便單點登出和多端管理

  • 實現滑動過期(自動續期),提升用戶體驗。


方案架構

流程圖

用戶登錄 -> 服務器生成UUID Token -> 保存Token對應的用戶ID到Redis并設置過期 -> 返回Token給客戶端 -> 客戶端請求時攜帶Token -> 網關/服務端從Redis驗證Token有效性 -> 每次請求時刷新Token過期時間(滑動過期) -> 用戶退出登錄時刪除Redis中的Token

關鍵代碼實現

1. 登錄接口

@PostMapping("/login")
public Result<UserVO> login(@RequestBody LoginRequest loginRequest) {// 認證邏輯驗證用戶名密碼(略)User user = userService.findByUsername(loginRequest.getUsername());if (user == null || !passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {return Result.fail("用戶名或密碼錯誤");}// 生成 UUID TokenString token = UUID.randomUUID().toString();// 構建 Redis 鍵String redisKey = RedisKeyConstant.LOGIN_TOKEN_PREFIX + token;// 保存用戶 ID 到 Redis,設置過期時間(30分鐘)stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(user.getId()), 30, TimeUnit.MINUTES);// 返回給前端UserVO userVO = new UserVO();userVO.setToken(token);// 其他用戶信息設置略return Result.ok(userVO);
}

2. 網關全局過濾器(校驗Token + 自動續期)

@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final StringRedisTemplate redisTemplate;private final AntPathMatcher antPathMatcher = new AntPathMatcher();private final long TOKEN_EXPIRE_MINUTES = 30;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String path = exchange.getRequest().getPath().toString();// 跳過無需認證路徑if (isExclude(path)) {return chain.filter(exchange);}List<String> tokenList = exchange.getRequest().getHeaders().get("Authorization");if (tokenList == null || tokenList.isEmpty()) {return unauthorized(exchange);}String token = tokenList.get(0);// Redis校驗Token是否有效String redisKey = RedisKeyConstant.LOGIN_TOKEN_PREFIX + token;String userId = redisTemplate.opsForValue().get(redisKey);if (userId == null) {return unauthorized(exchange);}// 續期(滑動過期)redisTemplate.expire(redisKey, TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);// 把用戶ID放入請求頭,供后端服務使用ServerHttpRequest newRequest = exchange.getRequest().mutate().header("user-info", userId).build();return chain.filter(exchange.mutate().request(newRequest).build());}private boolean isExclude(String path) {// 這里可以配置白名單路徑return path.startsWith("/public") || path.equals("/user/login");}private Mono<Void> unauthorized(ServerWebExchange exchange) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}@Overridepublic int getOrder() {return 0;}
}

3. 退出登錄接口

@PostMapping("/logout")
public Result<Void> logout(@RequestHeader("Authorization") String token) {if (token == null || token.isEmpty()) {return Result.fail("未登錄");}String redisKey = RedisKeyConstant.LOGIN_TOKEN_PREFIX + token;Boolean deleted = stringRedisTemplate.delete(redisKey);if (Boolean.TRUE.equals(deleted)) {return Result.ok();} else {return Result.fail("退出失敗或已過期");}
}

4. 前端請求示例(基于axios)

import axios from 'axios';const myAxios = axios.create({baseURL: 'http://localhost:9090',withCredentials: true,
});myAxios.interceptors.request.use(config => {const token = localStorage.getItem('token');if (token) {config.headers['Authorization'] = token;}return config;
});myAxios.interceptors.response.use(response => {if (response.data.code === 40101) {alert('未登錄,請重新登錄');window.location.href = '/user/login';}return response.data;
});export default myAxios;

總結

  • 通過Redis + UUID Token方案,避免了JWT token的復雜管理。

  • 支持服務器主動失效token,支持滑動過期,提升了安全性和用戶體驗。

  • 網關統一校驗token,簡化后端服務實現。

  • 適合對token管理要求較高,需要靈活控制用戶登錄狀態的項目。


未來展望

  • 可以結合Redis的Hash數據結構實現多端登錄管理。

  • 增加刷新token接口,實現無感刷新。

  • 結合Spring Security進行更細粒度的權限控制。


希望這篇文章對你有所幫助,歡迎點贊和關注!如果你也在用Spring Boot做項目,不妨試試這個思路,靈活又實用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82330.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82330.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82330.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MongoDB 快速整合 SpringBoot 示例

1.添加依賴<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spr…

Flyweight(享元)設計模式 軟考 享元 和 代理屬于結構型設計模式

1.目的&#xff1a;運用共享技術有效地支持大量細粒度的對象 Flyweight&#xff08;享元&#xff09;設計模式 是一種結構型設計模式&#xff0c;它的核心目的是通過共享對象來減少內存消耗&#xff0c;特別是在需要大量相似對象的場景中。Flyweight 模式通過將對象的共享細節與…

002大模型-提示詞工程,少樣本提示,角色扮演,思維鏈

一、提示詞工程 二、少樣本提示 三、角色扮演 四、思維鏈

華為OD機試真題——傳遞悄悄話(二叉樹最長路徑問題)(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳實現

2025 A卷 200分 題型 本專欄內全部題目均提供Java、python、JavaScript、C、C++、GO六種語言的最佳實現方式; 并且每種語言均涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、3個測試用例以及綜合分析; 本文收錄于專欄:《2025華為OD真題目錄+全流程解析+備考攻略+經驗分…

「讀書報告」Spark實時大數據分析

這本書是清華大學出版社2018年出版的&#xff0c;我是2020年讀的&#xff0c;說真的的&#xff0c;不怎么喜歡這本書&#xff0c;所以作者我都不想提。有的人可能會奇怪&#xff0c;ailx10&#xff0c;你一個搞網絡安全的&#xff0c;怎么會去讀大數據相關的書&#xff0c;哎&a…

2025 河北ICPC( D. 金泰園(二分)-- C.年少的誓約(公式轉化))

文章目錄 2025 河北ICPCD. 金泰園&#xff08;二分&#xff09;C.年少的誓約(公式轉化)總結 2025 河北ICPC 題目鏈接&#xff1a; Attachments - The 9th Hebei Collegiate Programming Contest - Codeforces sdccpc20250522 - Virtual Judge 賽時&#xff1a;5道 D. 金泰…

QT學習一

對于選擇qmake還是cmake&#xff0c;現在寫的暫時先用qmake 1.命名規范和快捷鍵 2.按鈕控件常用API //創建第一個按鈕QPushButton * btn new QPushButton;//讓btn對象 依賴在mywidget窗口中btn->setParent(this);//顯示文本btn->setText("第一個按鈕");//創建…

【Elasticsearch】給所索引創建多個別名

Elasticsearch 是可以給索引創建多個別名的。 為什么可以創建多個別名 1. 靈活性 - 別名可以為索引提供一個更易于理解的名稱&#xff0c;方便用戶根據不同的業務場景或用途來引用同一個索引。例如&#xff0c;一個索引可能同時服務于多個不同的應用程序或服務&#xff0c;通…

使用 OpenCV 實現哈哈鏡效果

在計算機視覺和圖像處理領域&#xff0c;OpenCV 提供了非常強大的圖像幾何變換能力&#xff0c;不僅可以用于糾正圖像&#xff0c;還能制造各種“有趣”的視覺效果。今天&#xff0c;我們就來實現一個經典的“哈哈鏡”效果&#xff0c;讓圖像像在游樂園里一樣被拉伸、壓縮、扭曲…

AI|Java開發 IntelliJ IDEA中接入本地部署的deepseek方法

目錄 連接本地部署的deepseek&#xff1a; IntelliJ IDEA中使用deepseek等AI&#xff1a; 用法一&#xff1a;讓AI寫代碼 用法二&#xff1a;選中這段代碼&#xff0c;右鍵&#xff0c;可以讓其解釋這段代碼的含義。這時顯示的解釋是英文的。 連接本地部署的deepseek&#…

如何使用兩塊硬盤作為 Ubuntu24 的系統盤,實現壞掉一塊不影響系統運行。

最近我想使用Ubuntu組一個NAS系統&#xff0c;想實現系統盤冗余&#xff0c;各位大佬可以給點建議嗎。 Deep Seek 為了實現兩塊硬盤作為 Ubuntu 24 系統盤的冗余配置&#xff08;RAID 1&#xff09;&#xff0c;確保一塊硬盤損壞時系統仍可運行&#xff0c;以下是詳細步驟&am…

【2025最新】虛擬機安裝macos,VMware在Windows11上安裝macOS 15完整圖文教程 - 新手也能輕松上手

引言 想體驗蘋果系統但不想買Mac電腦&#xff1f;別擔心&#xff01;本教程將手把手教你如何在Windows11環境下&#xff0c;通過VMware虛擬機安裝macOS Sequoia15系統。即使你是零基礎小白&#xff0c;按照這個步驟操作&#xff0c;也能輕松搞定&#xff01; 準備工作 在開始…

論文閱讀筆記——Emerging Properties in Unified Multimodal Pretraining

BAGEL 論文 商業閉源系統與學術/開源模型的差距很大&#xff0c;BAGEL 旨在通過開源統一架構大規模交錯數據主要解決&#xff1a; 架構割裂&#xff1a;理解/生成分屬兩條網絡&#xff0c;信息被壓縮在少量條件 token 中&#xff0c;長上下文推理受限。數據貧乏&#xff1a;主…

Go 語言基礎1 Slice,map,string

更多個人筆記見&#xff1a; github個人筆記倉庫 gitee 個人筆記倉庫 個人學習&#xff0c;學習過程中還會不斷補充&#xff5e; &#xff08;后續會更新在github上&#xff09; 文章目錄 stirng 字符串區分 rune&#xff0c;byte&#xff0c;string字符串操作strings 庫相關 f…

C# AI(Trae工具+claude3.5-sonnet) 寫前后端

這是一個AI 寫的前后端分離項目,通過AI編程&#xff0c;開發電商管理系統&#xff08;登陸、注冊&#xff09; 使用的AI工具為 Trae工具(字節國際版)claude3.5-sonnet(目前代碼最強模型) 前端為 vue3Bootstrap 后端為 C# net5.0(因為我電腦里面已經安裝了這個新版更好) do…

10G/25G PCS only mode for CoaXPress Over Fiber

背景 在CoaXPress Over Fiber的需求中, 需要利用XGMII的PCS 實現25G 數據速率的穩定傳輸&#xff0c;也就是不需要其MAC層&#xff0c;只保留PMA PCS層&#xff0c;借用其物理端口 線纜&#xff0c;實現其它協議的數據傳輸。 25G PCS 25GMII 的 TX/RX 時鐘頻率在 DDR&#xff…

掌握聚合函數:COUNT,MAX,MIN,SUM,AVG,GROUP BY和HAVING子句的用法,Where和HAVING的區別

對于Java后端開發來說&#xff0c;必須要掌握常用的聚合函數&#xff1a;COUNT&#xff0c;MAX&#xff0c;MIN&#xff0c;SUM&#xff0c;AVG&#xff0c;掌握GROUP BY和HAVING子句的用法&#xff0c;掌握Where和HAVING的區別&#xff1a; ? 一、常用聚合函數&#xff08;聚…

無人機飛行間隔安全智能評估、安全風險評估

無人機空中安全飛行評估需結合改進碰撞模型、蒙特卡洛仿真、安全間隔反推及動態避障策略&#xff0c;通過多機型分類與實時數據融合&#xff0c;實現從理論建模到實際部署的全流程管控&#xff0c;為城市低空密集飛行提供安全保障。 需求 無人機飛行間隔安全智能評估 無人機…

pdf圖片導出(Visio和Origin)

一、Visio 導入pdf格式圖片 1. 設計->大小&#xff0c;適應繪圖。 2. 文件->導出&#xff0c;導出為pdf格式。 上面兩部即可得到只包含圖的部分的pdf格式。 如果出現的有默認白邊&#xff0c;可以通過以下方式設置&#xff1a; 1. 文件->選項->自定義功能區->…

實現一個帶有授權碼和使用時間限制的Spring Boot項目

生成和驗證授權碼記錄授權時間和過期時間實現授權邏輯 以下是具體的實現方法&#xff1a; 1. 生成和驗證授權碼 可以使用加密技術生成和驗證授權碼。授權碼中可以包含有效期等信息&#xff0c;并使用密鑰進行簽名。 示例代碼&#xff1a; java復制代碼 import javax.crypt…