前言
在微服務架構中,1個系統會被拆分為了很多個微服務,
如果每1個微服務都直接對外暴露出來,讓用戶直接訪問這些微服務;
那么如何對用戶的身份和權限進行鑒定?如何對微服務中的訪問流量進行限流?
此時我們需要1個統一的入口(網關服務)以上問題將迎刃而解;

一、服務網關(Gateway)簡介
微服務的網關=路由轉發+過濾器
如果沒有網關的存在,我們只能在客戶端記錄每個微服務的地址,然后分別去呼叫,

以上架構,會存在著諸多的問題:
-
客戶端多次請求不同的微服務,增加客戶端代碼或配置撰寫的復雜性
-
認證復雜,每個服務都需要獨立認證,
-
存在跨域請求,在一定場景下處理相對復雜,
上面的這些問題可以借助API網關來解決,所謂的API網關,就是指系統的統一入口,它封裝了應用程式的內部結構,為客戶端提供統一服務,
一些與業務本身功能無關的公共邏輯可以在這里實作,諸如認證、鑒權、監控、路由轉發等等,
添加上API網關之后,系統的架構圖變成了如下所示:

Spring Cloud Gateway旨在為微服務架構提供一種簡單有效的統一的 API路由管理方式,
它不僅提供統一的路由方式,并且基于Filter鏈的方式提供了網關基本的功能,例如:安全,監控和限流,
微服務網關的作用
- 提供了統一訪問入口,降低了服務受攻擊面
- 提供了統一跨域解決方案
- 提供了統一日志記錄操作,可以進行統一監控
- 提供了統一權限認證支持
- 提供了微服務限流功能,可以保護微服務,防止雪崩效應發生
二、Gateway搭建

1.創建1個api-gateway模塊

2.pom依賴
我們使用的網關產品為spring-cloud框架提供的gateway;
服務網關需要呼叫服務注冊中心(Nacos)獲取服務提供者的呼叫地址;
<dependencies> <!--引入gateway網關--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--Nacos服務發現依賴--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
2.創建啟動類
package com.zhanggen.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
3.添加組態檔(application.yaml)
路由(Route) 是 gateway 中最基本的組件之一,表示一個具體的路由資訊載體,主要定義了下面的幾個資訊:
-
id,路由識別符號,區別于其他 Route,
-
uri,路由指向的目的地 uri,即客戶端請求最終被轉發到的微服務,
-
predicate,斷言的作用是進行條件判斷,只有斷言都回傳真,才會真正的執行路由,
-
filter,過濾器用于修改請求和回應資訊,
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 路由陣列[路由 就是指定當請求滿足什么條件的時候轉到哪個微服務]
- id: user-service-route # 當前路由的標識, 要求唯一
uri: lb://user-service # 請求要轉發到的地址
predicates: # 斷言(就是路由轉發要滿足的條件)
- Path=/user/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發
4.測驗網關路由轉發功能

啟動專案,并通過網關去訪問用戶微服務,現請求流程如下
- 用戶的請求http://127.0.0.1:7000/user/1到達GatWay
- GatWay摘取URL中的/user/1路徑進行路由匹配
- 匹配成功之后URL變成http://user-service/user/1
- GatWay呼叫客戶端負載均衡器(Ribin)去服務注冊中心(Nacos)拉取http://user-service/user/1對應的服務,進行負載均衡選擇
- 選擇出1個微服務之后http://user-service/user/1轉換成http://127.0.0.1:8081/user/1
- GatWay把用戶請求轉發到http://127.0.0.1:8081/user/1;
三、斷言(理解)
斷言就是1刀切,只有當前這1個條件判斷成功才能進行路由轉發,只有斷言都回傳真,才會真正的執行路由,
1.基于Datetime型別的斷言
# AfterRoutePredicateFactory: 接收一個日期引數,判斷請求日期是否晚于指定日期
# BeforeRoutePredicateFactory: 接收一個日期引數,判斷請求日期是否早于指定日期
# BetweenRoutePredicateFactory: 接收兩個日期引數,判斷請求日期是否在指定時間段內
- After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
2.基于遠程地址的斷言
# RemoteAddrRoutePredicateFactory:接收一個IP地址段,判斷請求主機地址是否在地址段中
- RemoteAddr=192.168.1.1/24
3.基于Cookie的斷言
# CookieRoutePredicateFactory:接收兩個引數,cookie 名字和一個正則運算式, 判斷請求cookie是否具有給定名稱且值與正則運算式匹配,
- Cookie=chocolate, ch.
4.基于Header的斷言
# HeaderRoutePredicateFactory:接收兩個引數,標題名稱和正則運算式, 判斷請求Header是否具有給定名稱且值與正則運算式匹配,
- Header=X-Request-Id, \d+
5.基于Host的斷言
# HostRoutePredicateFactory:接收一個引數,主機名模式,判斷請求的Host是否滿足匹配規則,
- Host=**.testhost.org
6.基于Path請求路徑的斷言
# PathRoutePredicateFactory:接收一個引數,判斷請求的URI部分是否滿足路徑規則,
- Path=/foo/{segment}
7.基于Query請求引數的斷言
# QueryRoutePredicateFactory :接收兩個引數,請求param和正則運算式, 判斷請求引數是否具有給定名稱且值與正則運算式匹配,
- Query=baz, ba.
8.使用
接下來我們驗證幾個內置斷言的使用:
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 路由陣列[路由 就是指定當請求滿足什么條件的時候轉到哪個微服務]
- id: user-service-route # 當前路由的標識, 要求唯一
uri: lb://user-service # 請求要轉發到的地址
predicates: # 斷言(就是路由轉發要滿足的條件)
- Path=/user/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發
- Before=2019-11-28T00:00:00.000+08:00 # 限制請求時間在2019-11-28之前
- Method=POST # 限制請求方式為POST
四、過濾器
Gateway也包含過濾器功能,網關服務的過濾器會對請求或回應進行攔截,完成一些通用操作,

1.過濾器執行時機
Gateway的過濾器中有2個執行時機:
-
PRE: 這種過濾器在請求被路由之前呼叫,可利用這種過濾器實作身份驗證、在集群中選擇請求的微服務、記錄除錯資訊等
-
POST:這種過濾器在路由到微服務以后執行,可用來為回應添加標準的HTTP Header、收集統計資訊和指標、將回應從微服務發送給客戶端等
2.過濾器型別
Gateway的Filter從作用范圍可分為2種
-
GatewayFilter:應用到單個路由或者一個分組的路由上
-
GlobalFilter:應用到所有的路由上
3.內置區域過濾器
區域過濾器是針對單個路由的過濾器,在SpringCloud Gateway中內置了很多不同型別的網關路由過濾器,具體如下:
| 過濾器工廠 | 作用 | 引數 |
|---|---|---|
| AddRequestHeader | 為原始請求添加Header | Header的名稱及值 |
| AddRequestParameter | 為原始請求添加請求引數 | 引數名稱及值 |
| AddResponseHeader | 為原始回應添加Header | Header的名稱及值 |
| DedupeResponseHeader | 剔除回應頭中重復的值 | 需要去重的Header名稱及去重策略 |
| Hystrix | 為路由引入Hystrix的斷路器保護 | HystrixCommand的名稱 |
| FallbackHeaders | 為fallbackUri的請求頭中添加具體的例外資訊 | Header的名稱 |
| PrefixPath | 為原始請求路徑添加前綴 | 前綴路徑 |
| PreserveHostHeader | 為請求添加一個preserveHostHeader=true的屬性,路由過濾器會檢查該屬性以決定是否要發送原始的Host | 無 |
| RequestRateLimiter | 用于對請求限流,限流演算法為令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
| RedirectTo | 將原始請求重定向到指定的URL | http狀態碼及重定向的url |
| RemoveHopByHopHeadersFilter | 為原始請求洗掉IETF組織規定的一系列Header | 默認就會啟用,可以通過配置指定僅洗掉哪些Header |
| RemoveRequestHeader | 為原始請求洗掉某個Header | Header名稱 |
| RemoveResponseHeader | 為原始回應洗掉某個Header | Header名稱 |
| RewritePath | 重寫原始的請求路徑 | 原始路徑正則運算式以及重寫后路徑的正則運算式 |
| RewriteResponseHeader | 重寫原始回應中的某個Header | Header名稱,值的正則運算式,重寫后的值 |
| SaveSession | 在轉發請求之前,強制執行WebSession::save操作 |
無 |
| secureHeaders | 為原始回應添加一系列起安全作用的回應頭 | 無,支持修改這些安全回應頭的值 |
| SetPath | 修改原始的請求路徑 | 修改后的路徑 |
| SetResponseHeader | 修改原始回應中某個Header的值 | Header名稱,修改后的值 |
| SetStatus | 修改原始回應的狀態碼 | HTTP 狀態碼,可以是數字,也可以是字串 |
| StripPrefix | 用于截斷原始請求的路徑 | 使用數字表示要截斷的路徑的數量 |
| Retry | 針對不同的回應進行重試 | retries、statuses、methods、series |
| RequestSize | 設定允許接收最大請求包的大小,如果請求包大小超過設定的值,則回傳 413 Payload Too Large |
請求包大小,單位為位元組,默認值為5M |
| ModifyRequestBody | 在轉發請求之前修改原始請求體內容 | 修改后的請求體內容 |
| ModifyResponseBody | 修改原始回應體的內容 | 修改后的回應體內容 |
4.內置區域過濾器的使用
我們只需要把過濾器配置在微服務的組態檔中即可生效;
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 路由陣列[路由 就是指定當請求滿足什么條件的時候轉到哪個微服務]
- id: user-service-route # 當前路由的標識, 要求唯一
uri: lb://user-service # 請求要轉發到的地址
predicates: # 斷言(就是路由轉發要滿足的條件)
- Path=/user/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發
filters:
- SetStatus=2000 # 修改回傳狀態
5.內置全域過濾器
全域過濾器作用于所有路由無需配置,通過全域過濾器可以實作對權限的統一校驗,安全性驗證等功能,
SpringCloud Gateway內部也是通過一系列的內置全域過濾器對整個路由轉發進行處理如下:

6.自定義全域過濾器(重點)
內置的過濾器已經可以完成大部分的功能,但是對于企業開發的一些業務功能處理,還是需要我們自己撰寫過濾器來實作的,
下面,我們一起通過代碼的形式自定義一個過濾器,去完成統一的權限校驗,
開發中的鑒權邏輯:
-
當客戶端第一次請求服務時,服務端對用戶進行資訊認證(登錄)
-
認證通過,將用戶資訊進行加密形成token,回傳給客戶端,作為登錄憑證
-
以后每次請求,客戶端都攜帶認證的token
-
服務端對token進行解密,判斷是否有效,
下面的我們自定義一個GlobalFilter,去校驗所有請求的請求引數中是否包含“token”,如何不包含請求引數“token”則不轉發路由,否則執行正常的邏輯,
package com.zhanggen.gateway.auth; import org.apache.commons.lang.StringUtils; 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.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; //全域認證過濾器 @Component public class AuthGlobalFilter implements GlobalFilter, Ordered { //認證邏輯 @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //請求引數數,獲取一個叫token的引數的值 String token = request.getParamter("token") String token = exchange.getRequest().getQueryParams().getFirst("token"); //判斷是否請求引數中攜帶了token if (StringUtils.isBlank(token)) { System.out.println("鑒權失敗"); exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//設定回應碼 resp.setStatus(401) return exchange.getResponse().setComplete();//回傳回應 } //呼叫chain.filter繼續向下游執行 return chain.filter(exchange); } //決定當前過濾器的執行級別, 陣列越小,優先級越高 @Override public int getOrder() { return 0; } }com.zhanggen.gateway.auth.AuthGlobalFilter
當過濾器的order值一樣時,區域過濾器 優先級高于 全域過濾器;
參考
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/498987.html
標籤:其他
上一篇:OO第四單元總結
下一篇:給女朋友看的訊息中間件
