Gateway:網關路由與登錄鑒權

在微服務架構中,用戶登錄和身份校驗的處理方式確實與單體應用有所不同。在單體架構中,一旦用戶通過身份驗證,其會話信息可以在整個應用范圍內共享,所有模塊都能訪問到用戶信息。然而,在微服務架構下,每個服務獨立部署且通常運行在不同的進程中,因此需要一種機制來確保用戶的身份信息能夠在各個微服務之間安全、高效地傳遞和驗證。

目錄

?

網關實現路由

網關登錄鑒權

鑒權思路

登錄校驗流程圖

網關過濾器詳解

實現網關過濾器

攔截器流程圖

服務信息鑒權


?

網關實現路由

問題:每個微服務都有不同的地址或端口,入口不同,請求不同數據時要訪問不同的入口,需要維護多個入口地址,前端無法調用nacos,無法實時更新服務列表。

解決方案:采用微服務網關,數據在網絡間傳輸,從一個網絡傳輸到另一網絡時就需要經過網關來做數據的路由和轉發。

在微服務中新建一個網關模塊,作為網關微服務

引入依賴(需要加入nacos依賴,讓nacos管理)

 <dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--網關--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--nacos discovery--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--負載均衡--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies>

application.yaml文件,配置路由

  gateway:routes:- id: item # 路由規則id,自定義,唯一uri: lb://item-service # 路由的目標服務,lb代表負載均衡,會從注冊中心拉取服務列表predicates: # 路由斷言,判斷當前請求是否符合當前規則,符合則路由到目標服務- Path=/items/**,/search/** # 這里是以請求路徑作為判斷規則

經測試成功

網關登錄鑒權

問題:單體架構時我們只需要完成一次用戶登錄、身份校驗,就可以在所有業務中獲取到用戶信息。但是,在微服務中,每個微服務都獨立部署,一般只有用戶微服務能校驗登錄信息,事實上,這是不安全的。

解決方案:既然網關是所有微服務的入口,一切請求都需要先經過網關。我們完全可以把登錄校驗的工作放到網關去做。

鑒權思路

登錄是基于JWT來實現的,校驗JWT的算法復雜,而且需要用到秘鑰。我們不可能讓個微服務都需要知道JWT的秘鑰,不安全。也不可能每個微服務重復編寫登錄校驗代碼、權限校驗代碼,麻煩。

所以,我們在網關和用戶服務保存秘鑰,開發登錄校驗功能。

登錄校驗流程圖

我們可以看到:前端——網關——后端服務

我們在網關層去實現過濾請求。

網關過濾器詳解

Gateway內部工作的基本原理:

如圖所示:

  1. 客戶端請求進入網關后由HandlerMapping對請求做判斷,找到與當前請求匹配的路由規則(Route),然后將請求交給WebHandler去處理

  2. WebHandler則會加載當前路由下需要執行的過濾器鏈(Filter chain),然后按照順序逐一執行過濾器(后面稱為Filter

  3. 圖中Filter被虛線分為左右兩部分,是因為Filter內部的邏輯分為prepost兩部分,分別會在請求路由到微服務之前之后被執行

  4. 只有所有Filterpre邏輯都依次順序執行通過后,請求才會被路由到微服務

  5. 微服務返回結果后,再倒序執行Filterpost邏輯

  6. 最終把響應結果返回

反正就是:我們需要在NettyRoutingFilter過濾器之前,在發起Request時,即pre時,定義一個過濾器,進行網關登錄校驗。

實現網關過濾器

我們采用全局過濾器,作用范圍是所有路由,即GlobalFilter。

package com.hmall.gateway.filters;import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@RequiredArgsConstructor
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();/*** 過濾器* @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.獲取請求對象ServerHttpRequest request = exchange.getRequest();// 2.判斷是否需要攔截if (isExclude(request.getPath().toString())) {// 放行return chain.filter(exchange);}// 3.獲取tokenString token = null;List<String> headers = request.getHeaders().get("authorization");if (headers != null && !headers.isEmpty()) {token = headers.get(0);}// 4.解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果無效,攔截ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}// 5.獲取用戶信息String userInfo = userId.toString();ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info",userInfo )).build();// 6.放行return chain.filter(swe);}/*** 判斷路徑是否需要攔截* @param antPath* @return*/private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}// 優先級@Overridepublic int getOrder() {return 0;}
}

因為我采用了JWT令牌進行校驗,所以引入了JWT工具類,進行令牌的獲取與解析。

至于AuthProperties是配置了不需要鑒權就能訪問的路徑。

經測試,網關已經可以完成登錄校驗并獲取登錄用戶身份信息。


問題:當網關將請求轉發到微服務時,微服務如何獲取用戶身份,我們不可能每個微服務都寫一個攔截器去得到用戶身份信息。

解決方案:將用戶信息以請求頭的方式傳遞到下游微服務。然后微服務可以從請求頭中獲取登錄用戶信息(上述代碼第五步已經完成了)。考慮到微服務內部可能很多地方都需要用到登錄用戶信息,因此我們可以利用SpringMVC的攔截器來實現登錄用戶信息獲取,并存入ThreadLocal。

攔截器流程圖

提供一個用于保存登錄用戶的ThreadLocal工具UserContext:

public class UserContext {private static final ThreadLocal<Long> tl = new ThreadLocal<>();/*** 保存當前登錄用戶信息到ThreadLocal* @param userId 用戶id*/public static void setUser(Long userId) {tl.set(userId);}/*** 獲取當前登錄用戶信息* @return 用戶id*/public static Long getUser() {return tl.get();}/*** 移除當前登錄用戶信息*/public static void removeUser(){tl.remove();}
}

在公用模塊下定義一個攔截器:

public class UserInfoInterceptor implements HandlerInterceptor {/*** 請求攔截器* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.獲取請求頭中的 用戶信息String userInfo = request.getHeader("user-info");// 2.判斷是否為空if (StringUtils.isNotBlank(userInfo)) {UserContext.setUser(Long.valueOf(userInfo));}// 3.放行return true;}/*** 響應攔截器* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserContext.removeUser();}
}

編寫SpringMVC的配置類,配置登錄攔截器:

@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}

注意:這個配置類默認是不會生效的,基于SpringBoot的自動裝配原理,我們要將其添加到resources目錄下的META-INF/spring.factories文件中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig

經測試,服務得到網關傳遞的用戶身份信息。

服務信息鑒權

問題:因為之前編寫的過濾器和攔截器功能,微服務可以輕松獲取登錄用戶信息。但是,有時候請求到達微服務后還需要調用其它多個微服務。我們沒有實現服務之間的用戶身份信息的傳遞。

解決方案:由于微服務獲取用戶信息是通過攔截器在請求頭中讀取,因此要想實現微服務之間的用戶信息傳遞,就必須在微服務發起調用時把用戶信息存入請求頭

因為我們之前微服務之間調用是基于OpenFeign來實現的,并不是我們自己發送的請求。所以我們可以采用Feign中提供的一個攔截器接口:feign.RequestInterceptor。

在公用模塊下定義一個userInfoRequestInterceptor

    /*** feign請求攔截器 微服務之間的遠程調用時,將當前登錄用戶的userId傳遞給目標服務* @return*/@Beanpublic RequestInterceptor userInfoRequestInterceptor() {return new RequestInterceptor() {public void apply(RequestTemplate requestTemplate) {Long userId = UserContext.getUser();if (userId != null) {requestTemplate.header("user-info", userId.toString());}}};}

總結:

  • 為了實現網關處簡便的登錄校驗,我們采用了GlobalFilter
  • 為了實現網關傳遞用戶信息到多個微服務,我們采用了UserInfoInterceptor
  • 為了實現微服務之間用戶身份信息傳遞,我們采用了userInfoRequestInterceptor。

?

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

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

相關文章

【結構光相機的精度極限】

1. 光源波長&#xff08;(\lambda)&#xff09; 光源波長是決定結構光相機精度極限的核心因素之一。根據光學衍射極限理論&#xff0c;光的波長越短&#xff0c;能夠分辨的細節越小&#xff0c;精度越高。 理論依據&#xff1a; 根據瑞利判據&#xff08;Rayleigh Criterion&…

Vision Transformer (ViT):將Transformer帶入計算機視覺的革命性嘗試(代碼實現)

Vision Transformer (ViT)&#xff1a;將Transformer帶入計算機視覺的革命性嘗試 作為一名深度學習研究者&#xff0c;如果你對自然語言處理&#xff08;NLP&#xff09;領域的Transformer架構了如指掌&#xff0c;那么你一定不會對它在序列建模中的強大能力感到陌生。然而&am…

【實戰ES】實戰 Elasticsearch:快速上手與深度實踐-8.1.1基于ES的語義搜索(BERT嵌入向量)

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 基于Elasticsearch與BERT的語義搜索架構設計與實戰1. 傳統搜索的局限性與語義搜索的崛起1.1 關鍵詞搜索 vs 語義搜索1.2 Elasticsearch向量檢索演進歷程關鍵版本特性對比 2.…

linux 學習筆記

# Linux學習筆記 ## 1 Linux入門 ### 1.1 概述 Linux內核最初只是芬蘭人在赫爾辛基大學上學時處于個人愛好而編寫的。 Linux是一套免費使用和自 由傳播的類Unix操作系統&#xff0c;是一個基于POSIX和UNIX的多用戶、多任務、支持多線程和多CPU的操作系統。Linux能運行主要的U…

Python個人學習筆記(14):函數(匿名函數、內置函數(下)、三元表達式)

九、匿名函數 lambda表達式 語法規則&#xff1a; 變量 lambda 參數1,參數2,…:返回值 例&#xff1a;用lambda簡化下述操作 def func(a,b):return ab ret func(1, 2) print(ret)代碼&#xff1a; fn lambda a,b:ab print(fn) print(fn(12,13))結果&#xff1a; <fun…

dns劫持是什么?常見的劫持類型有哪些?如何預防?

DNS劫持的定義 DNS劫持&#xff08;Domain Name System Hijacking&#xff09;是一種網絡攻擊手段&#xff0c;攻擊者通過篡改域名解析的過程&#xff0c;將用戶對某個域名的訪問請求重定向到錯誤或惡意的IP地址。這種攻擊可能導致用戶訪問到釣魚網站、惡意廣告頁面&#xff0…

prompt大師高效提示詞解析

Prompt大師李繼剛高效提示詞示例解析 一、「漢語新解」提示詞 核心結構 采用Lisp語言框架嵌套中文語義&#xff0c;通過(defun 新漢語老師 ()...)定義角色風格&#xff08;融合奧斯卡王爾德、魯迅的批判性語言&#xff09;&#xff0c;用(隱喻 (一針見血...))構建解釋邏輯鏈。…

基于 Vue 的Deepseek流式加載對話Demo

目錄 引言組件概述核心組件與功能實現1. 消息顯示組件&#xff08;Message.vue&#xff09;2. 輸入組件&#xff08;Input.vue&#xff09;3. 流式請求處理&#xff08;useDeepseek.ts&#xff09;4. 語音處理模塊&#xff08;Voice.vue&#xff09; 總結Demo Github 地址 引言…

RK3588 編譯 openssl

在編譯 OpenSSL 時,你需要確保你的系統環境已經配置好了所有必要的依賴和編譯工具。下面是一般步驟和一些常見問題的解決方案,特別是在使用 RK3588 這類的 ARM 處理器上。 1. 安裝依賴 首先,你需要安裝編譯 OpenSSL 所需的依賴。這通常包括編譯器(如 GCC)、make 工具、Per…

常見JVM命令

1. java -XX:PrintCommandLineFlags HelloGC 作用&#xff1a;打印 JVM 啟動時的命令行參數&#xff0c;包括用戶顯式設置的參數和 JVM 自動默認設置的參數。用于確認 JVM 實際使用的配置。 2. java -Xmn10M -Xms40M -Xmx60M -XX:PrintCommandLineFlags -XX:PrintGC -XX:Prin…

easy-poi導出and導入一對多數據excel

easy-poi導出and導入一對多數據excel 一、導入jar包 <!-- easy-poi --><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency> 二…

c#如何直接獲取json中的某個值

在 C# 中直接獲取 JSON 中的某個值,通常可以通過以下方法實現(以 Newtonsoft.Json 和 .NET 內置的 System.Text.Json 為例): 方法 1:使用 System.Text.Json(.NET 內置庫) using System.Text.Json;// 示例 JSON 字符串 string json = @"{""name"&qu…

WPS二次開發系列:Android 第三方應用如何獲取WPS端內文檔

1.需求場景 在項目開發中碰到這種情況&#xff0c;我們需要利用WPS的文檔管理能力&#xff0c;比如需要調用WPS的文件選擇器&#xff0c;來選擇文檔&#xff0c;同時需要得到WPS選擇的文檔結果返回給我們的應用。之前在網上找到了很久都沒有找到WPS移動端有相關的API接口文檔和…

Pytesseract識別圖片

1. Pytesseract識別圖片原理 1.1 Tesseract引擎工作原理 Tesseract OCR 引擎是一個功能強大的開源文字識別工具&#xff0c;其工作原理可以分為以下幾個關鍵步驟&#xff1a; 圖像預處理&#xff1a;Tesseract 首先對輸入的圖像進行預處理&#xff0c;包括灰度化、二值化、去…

Flutter 基礎組件 Text 詳解

目錄 1. 引言 2. 基本使用 3. 自定義樣式 4. 文本對齊與溢出控制 5. 外邊距 5.1 使用 Container 包裹 5.2 使用 Padding 組件 5.3 在 Row/Column 中使用 5.4 動態邊距調整 5.5 關鍵區別說明 5.6 設置 margin 無效 6. 結論 相關推薦 1. 引言 Text 組件是 Flutter 中…

Acknowledgment.nack方法重試消費kafka消息異常

文章目錄 問題示例異常 原因nack方法Acknowledgment接口實現類&#xff1a;ConsumerAcknowledgment實現類&#xff1a;ConsumerBatchAcknowledgment 解決方案1 批量消費指定index示例 2 單條消費示例 問題 使用BatchAcknowledgingMessageListener 批量消費Kafka消息&#xff0…

Java 反序列化 - commons collection 之困(一)

#01多余的碎碎念 說到 java 反序列化&#xff0c;去搜索的話能看到網上有很多分析關于 commons collection 利用鏈的文章&#xff0c;emm 我一開始看不懂&#xff0c;看到很多代碼的圖頭暈。 這篇文章的話其實是我跟著 p 神的文章一路走下來的&#xff0c;所以整個邏輯會按照…

python LLM工具包

阿里云鏡像pypi http://mirrors.aliyun.com/pypi/simple/ modelscope魔塔 pip install modelscope https://modelscope.cn/docs/models/download Sentence-transformers pip install -U sentence-transformers pip3 install torch -i https://pypi.tuna.tsinghua.edu.cn/sim…

Linux賬號和權限管理

用戶賬戶管理 理論 /etc/passwd 該目錄用于保存用戶名&#xff0c;宿主目錄&#xff0c;登錄shel等基本信息 /etc/shadow 該目錄用于保存 用戶密碼&#xff0c;賬戶有效期等信息 圖上每一行中都有用“&#xff1a;”隔斷的字段 字段含義&#xff1a; 第1字段:用戶賬號的名…