微服務的網關配置
1. 網關路由
1.1 網關
1.1.1 存在問題
單體架構時我們只需要完成一次用戶登錄、身份校驗,就可以在所有業務中獲取到用戶信息。而微服務拆分后,每個微服務都獨立部署,這就存在一些問題:每個微服務都需要編寫身份校驗、用戶信息獲取的接口,非常麻煩。
用戶身份校驗最好放在一個統一的地方,例如:網關。
1.1.2 認識網關
顧明思議,網關就是網絡的關口。數據在網絡間傳輸,從一個網絡傳輸到另一網絡時就需要經過網關來做數據的路由和轉發以及數據安全的校驗。
實現 java 微服務網關的技術:
- Netflix Zuul:早期實現,目前已經淘汰
- Spring Cloud Gateway:基于 Spring 的 WebFlux 技術,完全支持響應式編程,吞吐能力更強
Spring Cloud Gateway 使用參考官網
使用 Spring Cloud Gateway 實現網關,如下圖:

前端請求網關根據請求路徑路由到微服務,網關從 nacos 獲取微服務實例地址將請求轉發到具體的微服務實例上。
生產環境中網關也是集群部署,在網關前邊通過 nginx 進行負載均衡,如下圖:

1.2 實現網關路由
1.2.1 配置
創建網關工程;
引入依賴
<properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies><!-- common:通用工具服務,有則添加 --><dependency><groupId>com.hmall</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>
創建啟動類
package com.hmall.gateway;@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
修改配置文件
server:port: 8080
spring:application:name: gatewaycloud:nacos:server-addr: 192.168.101.68:8848gateway:routes:- id: item # 以 item-service 舉例,根據實際修改uri: lb://item-servicepredicates:- Path=/items/**
路由規則 routes 包括四個屬性,定義語法如下:
id
:路由的唯一標示predicates
:路由斷言,Predicates 是用于判斷請求是否滿足特定條件的組件。filters
:路由過濾條件,稍后講解。uri
:路由目標地址,lb://
代表負載均衡,從注冊中心獲取目標微服務的實例列表,并且負載均衡選擇一個訪問。-
:用于表示數組
1.2.2 配置項
predicates
屬性,也就是路由斷言。Spring Cloud Gateway 中支持的斷言類型有很多:
名稱 | 說明 |
---|---|
Cookie | 請求必須包含某些 cookie |
Header | 請求必須包含某些 header |
Host | 請求必須是訪問某個host(域名) |
Method | 請求方式必須是指定方式 |
Path | 請求路徑必須符合指定規則 |
weight | 權重處理 |
常用的類型就是 Header、Path,Header 的使用如 - Header=X-Request-Id, \d+
,說明請求頭中包含屬性 X-Request-Id
,且對應的值為數字。
2. 網關鑒權
2.1 認識網關鑒權
單體架構時我們只需要完成一次用戶登錄、身份校驗,就可以在所有業務中獲取到用戶信息。而微服務拆分后,每個微服務都獨立部署,不再共享數據。也就意味著每個微服務都需要做身份校驗,這顯然不可取。
我們的登錄是基于 JWT 來實現的,校驗 JWT 的算法復雜,而且需要用到密鑰。如果每個微服務都去做身份校驗,這就存在著兩大問題:
- 每個微服務都需要知道 JWT 的密鑰,不安全。
- 每個微服務重復編寫身份校驗代碼、權限校驗代碼,代碼重復不易維護。
網關鑒權是指在網關對請求進行身份驗證的過程。這個過程確保只有經過授權的用戶或設備才能訪問特定的服務或資源。

流程如下:
- 用戶登錄成功生成 token 并存儲在前端
- 前端攜帶 token 訪問網關
- 網關解析 token 中的用戶信息,網關將請求轉發到微服務,轉發時攜帶用戶信息
- 微服務從 http 頭信息獲取用戶信息
- 微服務之間遠程調用使用內部接口(無狀態接口)
2.2 網關內置過濾器
網關過濾器鏈中的過濾器有兩種:
GatewayFilter
:路由過濾器,作用范圍比較靈活,可以是任意指定的路由Route
。GlobalFilter
:全局過濾器,作用范圍是所有路由,不可配置。
FilteringWebHandler
在處理請求時,會將 GlobalFilter
裝飾為 GatewayFilter
,然后放到過濾器鏈中,排序以后依次執行。
Gateway
中內置了很多的 GatewayFilter
,詳情可以參考官方文檔:
常用的過濾器:StripPrefix
router- id: producturi: lb://item-servicepredicates:- Path=/product/**filters:- StripPrefix=1
StripPrefix=1
表示去除一級路徑前綴,使用 StripPrefix=1
后
請求:http://localhost:8080/product/items
經過網關轉換后,實際請求路徑:http://localhost:8081/items
2.3 自定義過濾器
2.3.1 GlobalFilter 過濾器
package com.hmall.gateway.filter;@Component
@Slf4j
public class PrintAnyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 編寫過濾器邏輯log.info("打印全局過濾器");// 放行return chain.filter(exchange);// 攔截// ServerHttpResponse response = exchange.getResponse();// 修改狀態碼// response.setRawStatusCode(401);// return response.setComplete();}@Overridepublic int getOrder() {// 過濾器執行順序,值越小,優先級越高return 0;}
}
全局過濾器不用在路由中配置。
package com.hmall.gateway.config;// 配置攔截路徑
@Data
@ConfigurationProperties(prefix = "hm.auth")
public class AuthProperties {private List<String> includePaths;private List<String> excludePaths;
}
2.3.2 GatewayFilter 過濾器
自定義 GatewayFilter
不是直接實現 GatewayFilter
,而是繼承 AbstractGatewayFilterFactory
。
注意:該類的名稱一定要以
GatewayFilterFactory
為后綴!
package com.hmall.gateway.filter;@Component
@Slf4j
public class FirstFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {@Overridepublic GatewayFilter apply(Object config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();log.info("請求路徑:{}",request.getPath());log.info("網關過濾器FirstFilterGatewayFilterFactory執行啦...");// 放行return chain.filter(exchange);// 攔截 返回401狀態碼// exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);// return exchange.getResponse().setComplete();}};}
}
路由過濾器需要在配置中配置過濾器:
- id: producturi: lb://item-servicepredicates:- Path=/product/**filters:- StripPrefix=1- FirstFilter # 此處直接以自定義的 GatewayFilterFactory 類名稱前綴類聲明過濾器
2.3.3 總結
兩種自定義過濾器的方式:
-
GatewayFilter
:路由過濾器作用范圍比較靈活,可以是任意指定的路由
Route
.繼承
AbstractGatewayFilterFactory
,并在路由配置中指定過濾器。過濾器的名稱規則:以
GatewayFilterFactory
作為后綴。 -
GlobalFilter
:全局過濾器作用范圍是所有路由,不可配置。
實現 GlobalFilter 接口。
實現 Ordered 接口可以指定過濾器順序,實現
getOrder()
方法,返回值越小優先級越好。
2.4 身份校驗過濾器
2.4.1 引入 JWT 工具類
參考資料
./
│
├── com.*.gateway/
│ │
│ ├── config/
│ │ ├── AuthProperties.java # 配置身份校驗需要攔截的路徑
│ │ ├── JwtProperties.java # 定義與JWT工具有關的屬性,比如秘鑰文件位置
│ │ └── SecurityConfig.java # 工具的自動裝配
│ │
│ └── util/
│ └── JwtTool.java # JWT工具,其中包含了校驗和解析 token 的功能
│
└── resources/│└── hmall.jks # 秘鑰文件
2.4.2 配置白名單
其中 AuthProperties
和 JwtProperties
所需的屬性要在 application.yaml
中配置
hm:jwt:location: classpath:hmall.jks # 秘鑰地址alias: hmall # 秘鑰別名password: hmall123 # 秘鑰文件密碼tokenTTL: 30m # 登錄有效期auth:excludePaths: # 無需身份校驗的路徑- /search/**- /users/login- /items/**
excludePaths 配置白名單地址,即無需身份校驗的路徑。
2.4.3 身份校驗過濾器
./
│
└── com.*.gateway/└── filter/└── AuthGlobalFilter.java
2.5 總結
網關身份校驗過濾器怎么實現?
我們項目中網關身份校驗過濾器使用 Spring cloud Gateway 的 GlobalFilter 實現,GlobalFilter 是一種全局過濾器。實現過程如下:
- 配置密鑰、白名單 等相關信息。
- 編寫身份校驗過濾器,實現 GlobalFilter 接口。
- 首先判斷請求地址是否是白名單地址,如果是則放行
- 取出 http 頭中的 token,然后校驗 token 的合法性
- 如果 token 合法則將 token 中的用戶信息向下傳給微服務
- 如果 token 不合法則拒絕訪問。