SpringCloudGateway 網(wǎng)關(guān)登錄校驗實(shí)現(xiàn)思路
單體架構(gòu)時我們只需要完成一次用戶登錄、身份校驗,就可以在所有業(yè)務(wù)中獲取到用戶信息。而微服務(wù)拆分后,每個微服務(wù)都獨(dú)立部署,不再共享數(shù)據(jù)。也就意味著每個微服務(wù)都需要做登錄校驗,這顯然不可取。
1. 思路分析
既然網(wǎng)關(guān)是所有微服務(wù)的入口,一切請求都需要先經(jīng)過網(wǎng)關(guān)。我們完全可以把登錄校驗的工作放到網(wǎng)關(guān)去做。
不過,這里存在幾個問題:
(1)網(wǎng)關(guān)路由是配置的,請求轉(zhuǎn)發(fā)是Gateway內(nèi)部代碼,我們?nèi)绾卧谵D(zhuǎn)發(fā)之前做登錄校驗?
可以使用網(wǎng)關(guān)過濾器。
(2)網(wǎng)關(guān)校驗JWT之后,如何將用戶信息傳遞給微服務(wù)?
由于網(wǎng)關(guān)發(fā)送請求到微服務(wù)依然采用的是Http
請求,因此我們可以將用戶信息以請求頭的方式傳遞到下游微服務(wù)。然后微服務(wù)可以從請求頭中獲取登錄用戶信息。
(3)微服務(wù)之間也會相互調(diào)用,這種調(diào)用不經(jīng)過網(wǎng)關(guān),又該如何傳遞用戶信息?
在微服務(wù)發(fā)起調(diào)用時把用戶信息存入請求頭。
2. 登錄校驗過濾器
白名單的配置:
在 application.yml
或 application.properties
文件中配置不需要攔截的路徑白名單。
gateway: ignoreUrls: - /auth/login - /auth/register - /public/**
在這個示例中,/auth/login
、/auth/register
、/public/**
等路徑將不需要進(jìn)行登錄校驗。
package com.cyt.gateway.filter; @Component // 聲明為Spring容器中的一個Bean @RequiredArgsConstructor // 生成構(gòu)造方法,注入final成員變量 @EnableConfigurationProperties(AuthProperties.class) // 啟用配置類AuthProperties的屬性注入 public class AuthGlobalFilter implements GlobalFilter, Ordered { // 從配置文件中注入白名單路徑列表,用于存放不需要JWT校驗的路徑 @Value("${gateway.ignoreUrls}") private List<String> ignoreUrls; private final JwtTool jwtTool; // JWT工具類,用于解析和校驗JWT @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 獲取Request對象,以訪問請求的相關(guān)信息 ServerHttpRequest request = exchange.getRequest(); // 2. 判斷請求路徑是否在白名單中(無需JWT校驗) if (isIgnoreUrl(request.getPath().toString())) { // 路徑在白名單中,直接放行請求 return chain.filter(exchange); } // 3. 獲取請求頭中的token(假設(shè)token放在"authorization"頭部) String token = null; List<String> headers = request.getHeaders().get("authorization"); if (!CollUtils.isEmpty(headers)) { // 如果請求頭包含authorization字段 token = headers.get(0); // 獲取第一個token值 } // 4. 使用jwtTool校驗并解析token Long userId = null; try { userId = jwtTool.parseToken(token); // 解析并獲取用戶ID } catch (UnauthorizedException e) { // 如果token無效或解析失敗,則返回401狀態(tài)碼并攔截請求 ServerHttpResponse response = exchange.getResponse(); response.setRawStatusCode(401); return response.setComplete(); // 返回空的響應(yīng)結(jié)束處理 } // TODO 5. 如果token有效,可在此處傳遞用戶信息(如通過exchange.getAttributes()) System.out.println("userId = " + userId); // 打印用戶ID供調(diào)試 // 6. 放行請求 return chain.filter(exchange); } /** * 判斷路徑是否在白名單中 * * @param path 請求路徑 * @return 如果路徑在白名單中返回true,否則返回false */ private boolean isIgnoreUrl(String path) { for (String ignoreUrl : ignoreUrls) { // 使用正則匹配白名單路徑中的通配符 "**" if (path.matches(ignoreUrl.replace("**", ".*"))) { return true; // 路徑匹配白名單中的某一項,返回true } } return false; // 沒有匹配到任何白名單路徑,返回false } @Override public int getOrder() { return 0; // 設(shè)置過濾器優(yōu)先級,值越小優(yōu)先級越高 } }
3. 保存用戶到請求頭
5.6.部分代碼可以參照下面修改:
// 5.傳遞用戶信息 // 將解析出的用戶ID轉(zhuǎn)換為字符串,以便可以在請求頭中傳遞 String userInfo = userId.toString(); // 使用 exchange.mutate() 方法創(chuàng)建一個新的 ServerWebExchange 對象, // 該對象將攜帶額外的請求頭 "user-info",包含用戶ID信息。 // .mutate() 方法用于復(fù)制當(dāng)前請求并進(jìn)行修改。 // 在這里,我們使用 request(builder -> builder.header(...)) 方式為請求添加一個新的頭部。 ServerWebExchange modifiedExchange = exchange.mutate() .request(builder -> builder.header("user-info", userInfo)) // 將 "user-info" 頭設(shè)置為用戶ID字符串 .build(); // 完成新的 ServerWebExchange 對象的構(gòu)建 // 6.放行 // 使用修改后的 ServerWebExchange 對象繼續(xù)執(zhí)行過濾鏈。 // 這樣,下游服務(wù)可以通過 "user-info" 請求頭獲取到用戶ID信息。 return chain.filter(modifiedExchange);
4. OpenFeign傳遞用戶
在微服務(wù)發(fā)起調(diào)用時把用戶信息存入請求頭。
如何才能讓每一個由OpenFeign發(fā)起的請求自動攜帶登錄用戶信息呢?
這里要借助Feign中提供的一個攔截器接口:feign.RequestInterceptor
我們只需要實(shí)現(xiàn)這個接口,然后實(shí)現(xiàn)apply方法,利用RequestTemplate
類來添加請求頭,將用戶信息保存到請求頭中。
可以在OpenFeign的配置類中添加一個Bean:
@Bean public RequestInterceptor userInfoRequestInterceptor(){ // 定義一個 Feign 的 RequestInterceptor Bean,用于在請求發(fā)出前執(zhí)行自定義攔截操作 return new RequestInterceptor() { @Override public void apply(RequestTemplate template) { // 獲取當(dāng)前登錄用戶的ID // UserContext 是一個上下文工具類,用于存儲和獲取當(dāng)前線程的用戶信息 Long userId = UserContext.getUser(); // 檢查用戶ID是否為 null,如果為 null 則表示用戶未登錄或無法獲取用戶信息 if(userId == null) { // 如果用戶ID為空,不做任何處理,直接返回,跳過攔截器邏輯 return; } // 如果用戶ID不為空,將用戶ID作為請求頭添加到 Feign 請求中 // "user-info" 是請求頭的鍵,下游微服務(wù)可以從該請求頭中獲取用戶ID template.header("user-info", userId.toString()); } }; }
5. 總結(jié)
思路分析中提到的三個問題,目前已經(jīng)全部解決。
接下來,微服務(wù)需要用戶信息,只需要編寫攔截器,獲取用戶信息并保存到ThreadLocal中,然后放行即可。
由于每個微服務(wù)都有獲取登錄用戶的需求,因此攔截器可以直接寫在common公共服務(wù)
中,并寫好自動裝配。這樣微服務(wù)只需要引入common
就可以直接具備攔截器功能,無需重復(fù)編寫。
到此這篇關(guān)于SpringCloudGateway 網(wǎng)關(guān)登錄校驗的文章就介紹到這了,更多相關(guān)SpringCloudGateway 登錄校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中Random簡介_動力節(jié)點(diǎn)Java學(xué)院整理
本文詳細(xì)給大家介紹了Java中Random簡介相關(guān)知識,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-06-06java實(shí)現(xiàn)追加內(nèi)容到文件末尾的常用方法分析
這篇文章主要介紹了java實(shí)現(xiàn)追加內(nèi)容到文件末尾的常用方法,結(jié)合具體實(shí)例分析了java文件流及寫入指針等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10詳解Spring數(shù)據(jù)緩存注解@Cacheable、@CachePut、@CacheEvict
這篇文章主要介紹了詳解Spring數(shù)據(jù)緩存注解@Cacheable、CachePut、@CacheEvict,當(dāng)以一組參數(shù)第一次調(diào)用某個方法時,返回值會被保存在緩存中,如果這個方法再次以相同的參數(shù)進(jìn)行調(diào)用時,這個返回值會從緩存中查詢獲取,需要的朋友可以參考下2023-07-07將一個數(shù)組按照固定大小進(jìn)行拆分成數(shù)組的方法
下面小編就為大家?guī)硪黄獙⒁粋€數(shù)組按照固定大小進(jìn)行拆分成數(shù)組的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11java 各種數(shù)據(jù)類型的互相轉(zhuǎn)換實(shí)例代碼
這篇文章主要介紹了java 各種數(shù)據(jù)類型的互相轉(zhuǎn)換實(shí)例代碼,需要的朋友可以參考下2020-10-10關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序
這篇文章主要介紹了關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09java方法重寫和super關(guān)鍵字實(shí)例詳解
這篇文章主要介紹了java方法重寫和super關(guān)鍵字實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03關(guān)于Spring Cloud 本地屬性覆蓋的問題
這篇文章主要介紹了關(guān)于Spring Cloud 本地屬性覆蓋的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03