Spring Cloud Gateway詳細(xì)使用最佳實踐
1. Spring Cloud Gateway 是什么?
Spring Cloud Gateway 是 Spring Cloud 生態(tài)系統(tǒng)中的現(xiàn)代化 API 網(wǎng)關(guān)組件,用于構(gòu)建微服務(wù)架構(gòu)中的統(tǒng)一入口網(wǎng)關(guān)。它基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.x 構(gòu)建,采用響應(yīng)式編程模型,提供高性能、非阻塞式的 API 路由和橫切關(guān)注點(diǎn)處理能力。
1.1 技術(shù)架構(gòu)基礎(chǔ)
- 響應(yīng)式編程:基于 Project Reactor 的響應(yīng)式流處理
- WebFlux:使用 Spring WebFlux 而非傳統(tǒng)的 Servlet 模型
- 函數(shù)式編程:支持 Java 8+ 的函數(shù)式編程風(fēng)格
- 高性能:相比 Zuul 1.x(阻塞式),性能提升顯著
1.2 在微服務(wù)架構(gòu)中的定位
客戶端 → Spring Cloud Gateway → 微服務(wù)A
→ 微服務(wù)B
→ 微服務(wù)C作為所有微服務(wù)的統(tǒng)一入口點(diǎn),客戶端只需要與網(wǎng)關(guān)交互,無需知道后端服務(wù)的具體位置。
2. Spring Cloud Gateway 的作用
2.1 核心作用
- 統(tǒng)一入口:為所有微服務(wù)提供單一訪問入口
- 路由轉(zhuǎn)發(fā):根據(jù)配置規(guī)則將請求路由到對應(yīng)的服務(wù)
- 負(fù)載均衡:集成 Spring Cloud LoadBalancer 實現(xiàn)服務(wù)發(fā)現(xiàn)和負(fù)載均衡
- 協(xié)議適配:支持 HTTP/HTTPS、WebSocket 等協(xié)議
- 監(jiān)控指標(biāo):收集請求日志、性能指標(biāo)等監(jiān)控數(shù)據(jù)
2.2 橫切關(guān)注點(diǎn)處理
- 認(rèn)證(Authentication):統(tǒng)一處理用戶身份驗證
- 安全防護(hù):防重放攻擊、IP 黑白名單、基礎(chǔ)安全頭設(shè)置
- 限流熔斷:實現(xiàn)請求限流、服務(wù)降級等保護(hù)機(jī)制
- 請求/響應(yīng)修改:修改請求頭、響應(yīng)頭等
3. Spring Cloud Gateway 的特點(diǎn)
3.1 技術(shù)特點(diǎn)
- 響應(yīng)式非阻塞:基于 Reactor 的異步非阻塞處理模型
- 高性能:單機(jī)可處理數(shù)萬 QPS
- 靈活路由:支持多種路由匹配方式(Path、Host、Method、Header 等)
- 豐富過濾器:內(nèi)置多種過濾器,支持自定義過濾器
- 動態(tài)配置:支持運(yùn)行時動態(tài)修改路由配置
- 服務(wù)發(fā)現(xiàn)集成:無縫集成 Eureka、Consul、Nacos 等注冊中心
3.2 功能組件
- Route(路由):定義請求如何被轉(zhuǎn)發(fā)到目標(biāo)服務(wù)
- Predicate(斷言):匹配 HTTP 請求的各種條件
- Filter(過濾器):修改請求和響應(yīng)的邏輯
- GlobalFilter(全局過濾器):應(yīng)用于所有路由的過濾器
4. 網(wǎng)關(guān)應(yīng)該做什么 vs 不應(yīng)該做什么
4.1 ? 網(wǎng)關(guān)應(yīng)該做的事情
4.1.1 路由和轉(zhuǎn)發(fā)
- 根據(jù)路徑、主機(jī)名等條件路由請求
- 實現(xiàn)負(fù)載均衡和服務(wù)發(fā)現(xiàn)
- 處理協(xié)議轉(zhuǎn)換(如 WebSocket)
4.1.2 基礎(chǔ)安全(認(rèn)證層面)
- 身份認(rèn)證:驗證 JWT Token、API Key 等的有效性
- Token 驗證:檢查簽名、過期時間、基本格式
- 安全頭處理:添加 X-Forwarded-For、X-Real-IP 等頭信息
- 基礎(chǔ)防護(hù):IP 黑白名單、防刷、防重放攻擊
4.1.3 運(yùn)維和監(jiān)控
- 統(tǒng)一日志:記錄訪問日志、請求響應(yīng)時間
- 指標(biāo)收集:收集 QPS、響應(yīng)時間、錯誤率等指標(biāo)
- 健康檢查:提供網(wǎng)關(guān)自身的健康檢查端點(diǎn)
- 限流熔斷:基于 Redis 或內(nèi)存的請求限流
4.1.4 請求/響應(yīng)處理
- 路徑重寫:StripPrefix、PrefixPath 等
- 頭信息修改:添加、刪除、修改請求/響應(yīng)頭
- 重試機(jī)制:對失敗請求進(jìn)行重試
4.2 ? 網(wǎng)關(guān)不應(yīng)該做的事情
4.2.1 業(yè)務(wù)權(quán)限控制(授權(quán))
- 不應(yīng)該進(jìn)行細(xì)粒度權(quán)限驗證(如:用戶A能否訪問用戶B的數(shù)據(jù))
- 不應(yīng)該驗證業(yè)務(wù)級別的權(quán)限(如:是否有刪除權(quán)限、編輯權(quán)限)
- 不應(yīng)該替代業(yè)務(wù)服務(wù)的安全邏輯
4.2.2 業(yè)務(wù)邏輯處理
- 不應(yīng)該包含業(yè)務(wù)邏輯(如:訂單狀態(tài)驗證、庫存檢查)
- 不應(yīng)該調(diào)用業(yè)務(wù)數(shù)據(jù)庫進(jìn)行復(fù)雜查詢
- 不應(yīng)該處理業(yè)務(wù)數(shù)據(jù)轉(zhuǎn)換
4.2.3 復(fù)雜數(shù)據(jù)處理
- 不應(yīng)該解析和驗證復(fù)雜的請求體
- 不應(yīng)該進(jìn)行業(yè)務(wù)數(shù)據(jù)的校驗和轉(zhuǎn)換
- 不應(yīng)該緩存業(yè)務(wù)數(shù)據(jù)
4.3 ??? 正確的安全分層架構(gòu)
客戶端 → 網(wǎng)關(guān)層(Authentication) → 業(yè)務(wù)服務(wù)層(Authorization)
↓ ↓
驗證"你是誰" 驗證"你能做什么"
Token有效性驗證 業(yè)務(wù)權(quán)限驗證
基礎(chǔ)安全防護(hù) 數(shù)據(jù)權(quán)限控制5. 詳細(xì)使用示例
5.1 基礎(chǔ)環(huán)境搭建
5.1.1 Maven 依賴配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-gateway-demo</artifactId>
<version>1.0.0</version>
<name>spring-cloud-gateway-demo</name>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<!-- Spring Cloud Gateway 核心依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服務(wù)發(fā)現(xiàn)客戶端(用于集成注冊中心) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Actuator 監(jiān)控端點(diǎn) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Redis 依賴(用于限流) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>5.1.2 基礎(chǔ)配置文件 (application.yml)
server:
port: 9000 # 網(wǎng)關(guān)服務(wù)端口
spring:
application:
name: api-gateway # 應(yīng)用名稱
cloud:
gateway:
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*" # 允許所有源
allowedMethods: "*" # 允許所有HTTP方法
allowedHeaders: "*" # 允許所有請求頭
allowCredentials: true # 允許攜帶憑證
# 路由配置
routes:
# 用戶服務(wù)路由 - 網(wǎng)關(guān)只負(fù)責(zé)路由和基礎(chǔ)認(rèn)證
- id: user-service-route
uri: http://localhost:8081
predicates:
- Path=/api/users/** # 匹配用戶相關(guān)路徑
filters:
- StripPrefix=2 # 去掉 /api/users 前綴,實際轉(zhuǎn)發(fā)到 /**
# 訂單服務(wù)路由
- id: order-service-route
uri: http://localhost:8082
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=2
# Actuator 監(jiān)控配置
management:
endpoints:
web:
exposure:
include: '*' # 暴露所有監(jiān)控端點(diǎn)
endpoint:
gateway:
enabled: true # 啟用網(wǎng)關(guān)管理端點(diǎn)5.2 路由配置詳解
5.2.1 Java 代碼配置路由
package com.example.gateway.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 網(wǎng)關(guān)路由配置類
* 使用 Java 代碼方式配置路由,相比配置文件更加靈活
* 注意:這里只配置路由規(guī)則,不包含業(yè)務(wù)權(quán)限邏輯
*/
@Configuration
public class GatewayRouteConfig {
/**
* 配置自定義路由規(guī)則
* @param builder RouteLocatorBuilder 用于構(gòu)建路由
* @return RouteLocator 路由定位器
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 用戶服務(wù)路由
.route("user-service", r -> r
.path("/api/users/**") // 匹配路徑
.uri("http://localhost:8081") // 目標(biāo)服務(wù)地址
.filters(f -> f.stripPrefix(2)) // 去掉前兩個路徑段 (/api/users)
)
// 訂單服務(wù)路由
.route("order-service", r -> r
.path("/api/orders/**")
.uri("http://localhost:8082")
.filters(f -> f.stripPrefix(2))
)
// 公開API路由(無需認(rèn)證)
.route("public-api", r -> r
.path("/api/public/**")
.uri("http://localhost:8083")
.filters(f -> f.stripPrefix(2))
)
.build();
}
}5.3 斷言(Predicates)使用示例
package com.example.gateway.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 各種斷言使用示例
* 斷言用于定義路由匹配條件,只有滿足條件的請求才會被路由
* 這些都是基礎(chǔ)的路由條件,不涉及業(yè)務(wù)邏輯
*/
@Configuration
public class PredicateConfig {
@Bean
public RouteLocator predicateRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 1. Path 斷言:基于URL路徑匹配
.route("path-route", r -> r
.path("/api/v1/**", "/api/v2/**") // 支持多個路徑模式
.uri("http://localhost:8081")
)
// 2. Method 斷言:基于HTTP方法匹配
.route("method-route", r -> r
.path("/api/method/**")
.and()
.method("GET", "POST") // 只匹配GET和POST請求
.uri("http://localhost:8082")
)
// 3. Header 斷言:基于請求頭匹配
.route("header-route", r -> r
.path("/api/header/**")
.and()
.header("X-API-Version", "v1") // 必須包含指定請求頭
.uri("http://localhost:8083")
)
// 4. Query 斷言:基于查詢參數(shù)匹配
.route("query-route", r -> r
.path("/api/query/**")
.and()
.query("version", "1.0") // 查詢參數(shù) version=1.0
.uri("http://localhost:8084")
)
// 5. Host 斷言:基于Host頭匹配
.route("host-route", r -> r
.host("api.example.com", "api.test.com") // 匹配指定域名
.uri("http://localhost:8085")
)
.build();
}
}5.4 過濾器(Filters)使用示例
5.4.1 內(nèi)置過濾器配置
package com.example.gateway.config;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 內(nèi)置過濾器使用示例
* 過濾器用于修改請求和響應(yīng),但只做基礎(chǔ)處理,不涉及業(yè)務(wù)邏輯
*/
@Configuration
public class FilterConfig {
@Bean
public RouteLocator filterRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 1. 添加請求頭過濾器
.route("add-header-route", r -> r
.path("/api/add-header/**")
.filters(f -> f
// 添加網(wǎng)關(guān)來源標(biāo)識
.addRequestHeader("X-Gateway-Source", "spring-cloud-gateway")
// 添加請求時間戳
.addRequestHeader("X-Request-Timestamp",
String.valueOf(System.currentTimeMillis()))
)
.uri("http://localhost:8081")
)
// 2. 添加響應(yīng)頭過濾器
.route("add-response-header-route", r -> r
.path("/api/add-response-header/**")
.filters(f -> f
.addResponseHeader("X-Gateway-Processed", "true")
)
.uri("http://localhost:8082")
)
// 3. 路徑重寫過濾器
.route("strip-prefix-route", r -> r
.path("/api/strip/**")
.filters(f -> f.stripPrefix(1)) // 去掉第一個路徑段
.uri("http://localhost:8083")
)
// 4. 限流過濾器(使用Redis)
.route("rate-limiter-route", r -> r
.path("/api/rate-limiter/**")
.filters(f -> f
.requestRateLimiter()
.rateLimiter(RedisRateLimiter.class)
// 配置限流參數(shù):每秒5個請求,突發(fā)容量10個
.configure(c -> c.setBurstCapacity(10).setReplenishRate(5))
)
.uri("http://localhost:8084")
)
// 5. 重試過濾器
.route("retry-route", r -> r
.path("/api/retry/**")
.filters(f -> f.retry(3)) // 失敗時重試3次
.uri("http://localhost:8085")
)
.build();
}
}5.5 網(wǎng)關(guān)安全配置(正確的做法)
5.5.1 網(wǎng)關(guān)層認(rèn)證過濾器(只做身份認(rèn)證)
package com.example.gateway.filter;
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.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
/**
* 網(wǎng)關(guān)認(rèn)證過濾器 - 只負(fù)責(zé)身份認(rèn)證,不負(fù)責(zé)權(quán)限授權(quán)
*
* 職責(zé)范圍:
* ? 驗證JWT Token的有效性(簽名、過期時間)
* ? 驗證Token的基本格式
* ? 將用戶信息傳遞給下游服務(wù)
* ? 不進(jìn)行業(yè)務(wù)權(quán)限驗證
* ? 不調(diào)用業(yè)務(wù)數(shù)據(jù)庫
* ? 不處理復(fù)雜的業(yè)務(wù)邏輯
*/
@Component
public class AuthenticationGlobalFilter implements GlobalFilter, Ordered {
// 公開路徑列表(無需認(rèn)證的路徑)
private static final List<String> PUBLIC_PATHS = Arrays.asList(
"/api/public/**",
"/api/auth/login",
"/api/auth/register",
"/actuator/**"
);
/**
* 全局過濾器執(zhí)行邏輯
* @param exchange 當(dāng)前請求交換對象
* @param chain 過濾器鏈
* @return Mono<Void> 響應(yīng)式返回
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String requestPath = exchange.getRequest().getURI().getPath();
// 1. 檢查是否為公開路徑,如果是則直接放行
if (isPublicPath(requestPath)) {
return chain.filter(exchange);
}
// 2. 從請求頭中提取認(rèn)證令牌
String token = extractToken(exchange);
if (token == null || token.isEmpty()) {
return handleUnauthorized(exchange, "Missing authentication token");
}
try {
// 3. 驗證令牌有效性(只驗證簽名和過期時間,不驗證業(yè)務(wù)權(quán)限)
if (!validateTokenSignatureAndExpiry(token)) {
return handleUnauthorized(exchange, "Invalid or expired token");
}
// 4. 從令牌中提取用戶基本信息
String userId = extractUserIdFromToken(token);
String userRoles = extractUserRolesFromToken(token);
// 5. 將用戶信息添加到請求頭,傳遞給下游業(yè)務(wù)服務(wù)
// 下游服務(wù)將基于這些信息進(jìn)行具體的權(quán)限驗證
ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
.header("X-User-Id", userId)
.header("X-User-Roles", userRoles)
.header("X-Authenticated", "true")
.build();
return chain.filter(exchange.mutate().request(modifiedRequest).build());
} catch (Exception e) {
return handleUnauthorized(exchange, "Authentication processing failed: " + e.getMessage());
}
}
/**
* 判斷路徑是否為公開路徑
* @param path 請求路徑
* @return 是否為公開路徑
*/
private boolean isPublicPath(String path) {
return PUBLIC_PATHS.stream()
.anyMatch(publicPath -> path.matches(publicPath.replace("**", ".*")));
}
/**
* 從請求中提取認(rèn)證令牌
* 支持 Bearer Token 和自定義 Header
*/
private String extractToken(ServerWebExchange exchange) {
// 優(yōu)先從 Authorization 頭獲取
String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7); // 去掉 "Bearer " 前綴
}
// 也可以從自定義頭或查詢參數(shù)獲取
String tokenHeader = exchange.getRequest().getHeaders().getFirst("X-API-Token");
if (tokenHeader != null) {
return tokenHeader;
}
return null;
}
/**
* 驗證令牌簽名和過期時間
* 注意:這里只做基礎(chǔ)驗證,不做業(yè)務(wù)權(quán)限驗證
*/
private boolean validateTokenSignatureAndExpiry(String token) {
// 實際項目中應(yīng)該使用 JWT 庫進(jìn)行驗證
// 這里簡化處理,實際應(yīng)該驗證簽名、過期時間等
try {
// 偽代碼:驗證 JWT 簽名和過期時間
// Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return token.length() > 20; // 簡單驗證
} catch (Exception e) {
return false;
}
}
/**
* 從令牌中提取用戶ID
* 實際項目中應(yīng)該解析 JWT payload
*/
private String extractUserIdFromToken(String token) {
// 偽代碼:從 JWT 中提取用戶ID
return "user123"; // 簡化處理
}
/**
* 從令牌中提取用戶角色
* 實際項目中應(yīng)該解析 JWT payload 中的角色信息
*/
private String extractUserRolesFromToken(String token) {
// 偽代碼:從 JWT 中提取角色
return "USER,PREMIUM"; // 簡化處理
}
/**
* 處理未授權(quán)請求
*/
private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
exchange.getResponse().getHeaders().add("Content-Type", "application/json");
String response = "{\"error\":\"Unauthorized\",\"message\":\"" + message + "\"}";
return exchange.getResponse().writeWith(
Mono.just(exchange.getResponse().bufferFactory().wrap(response.getBytes()))
);
}
/**
* 設(shè)置過濾器執(zhí)行順序
* 數(shù)值越小,優(yōu)先級越高
*/
@Override
public int getOrder() {
return -100; // 在很早的階段執(zhí)行認(rèn)證
}
}5.5.2 業(yè)務(wù)服務(wù)層權(quán)限控制(真正的授權(quán))
// 用戶服務(wù)中的權(quán)限控制示例
package com.example.userservice.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.http.HttpStatus;
/**
* 用戶控制器 - 在業(yè)務(wù)服務(wù)中進(jìn)行真正的權(quán)限驗證
*
* 網(wǎng)關(guān)已經(jīng)完成了身份認(rèn)證,這里進(jìn)行業(yè)務(wù)級別的權(quán)限授權(quán)
*/
@RestController
@RequestMapping("/api")
public class UserController {
/**
* 獲取用戶詳情
* 需要驗證當(dāng)前用戶是否有權(quán)限訪問目標(biāo)用戶
*/
@GetMapping("/users/{userId}")
public User getUser(@RequestHeader("X-User-Id") String currentUserId,
@RequestHeader("X-User-Roles") String userRoles,
@PathVariable String userId) {
// 1. 驗證是否是訪問自己的信息(普通用戶只能訪問自己)
if (!currentUserId.equals(userId)) {
// 2. 如果不是訪問自己,檢查是否具有管理員角色
if (!userRoles.contains("ADMIN")) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
"Insufficient permissions to access this user data");
}
}
// 3. 執(zhí)行業(yè)務(wù)邏輯
return userService.findById(userId);
}
/**
* 刪除用戶 - 需要管理員權(quán)限
* 使用 Spring Security 注解進(jìn)行權(quán)限控制
*/
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/users/{userId}")
public void deleteUser(@PathVariable String userId) {
userService.delete(userId);
}
}5.6 日志和監(jiān)控配置
5.6.1 訪問日志過濾器
package com.example.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
/**
* 訪問日志全局過濾器
* 記錄請求的基本信息,用于監(jiān)控和審計
* 注意:只記錄基礎(chǔ)信息,不記錄敏感業(yè)務(wù)數(shù)據(jù)
*/
@Component
public class AccessLogGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 記錄請求開始時間
long startTime = System.currentTimeMillis();
String path = exchange.getRequest().getURI().getPath();
String method = exchange.getRequest().getMethodValue();
String clientIp = getClientIp(exchange);
System.out.println(String.format(
"[ACCESS-LOG] %s | %s | %s | %s",
LocalDateTime.now(), method, path, clientIp
));
// 繼續(xù)處理請求,并記錄響應(yīng)時間
return chain.filter(exchange).doOnTerminate(() -> {
long duration = System.currentTimeMillis() - startTime;
System.out.println(String.format(
"[ACCESS-LOG] Response time: %d ms for %s", duration, path
));
});
}
private String getClientIp(ServerWebExchange exchange) {
String xForwardedFor = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return exchange.getRequest().getRemoteAddress() != null ?
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() : "unknown";
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1; // 在最后執(zhí)行,確保能獲取到完整信息
}
}5.7 完整的啟動類
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Spring Cloud Gateway 網(wǎng)關(guān)啟動類
*
* 設(shè)計原則:
* ? 網(wǎng)關(guān)只負(fù)責(zé):路由轉(zhuǎn)發(fā)、身份認(rèn)證、基礎(chǔ)安全、監(jiān)控日志
* ? 網(wǎng)關(guān)不負(fù)責(zé):業(yè)務(wù)權(quán)限、業(yè)務(wù)邏輯、數(shù)據(jù)驗證
*
* 啟動后可用端點(diǎn):
* - 網(wǎng)關(guān)服務(wù):http://localhost:9000
* - 路由信息:http://localhost:9000/actuator/gateway/routes
* - 健康檢查:http://localhost:9000/actuator/health
*/
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
System.out.println("=====================================");
System.out.println("Spring Cloud Gateway 啟動成功!");
System.out.println("網(wǎng)關(guān)地址: http://localhost:9000");
System.out.println("路由管理: http://localhost:9000/actuator/gateway/routes");
System.out.println("=====================================");
}
}6. 最佳實踐總結(jié)
6.1 網(wǎng)關(guān)職責(zé)邊界
- 網(wǎng)關(guān)層:路由 + 認(rèn)證 + 基礎(chǔ)安全 + 監(jiān)控
- 業(yè)務(wù)層:授權(quán) + 業(yè)務(wù)邏輯 + 數(shù)據(jù)驗證
6.2 性能優(yōu)化建議
- 合理配置線程池和連接池
- 避免在過濾器中執(zhí)行耗時操作
- 使用緩存減少重復(fù)計算
6.3 安全最佳實踐
- 網(wǎng)關(guān)只做身份認(rèn)證,不做權(quán)限授權(quán)
- 敏感信息不要在網(wǎng)關(guān)層處理
- 使用 HTTPS 保護(hù)傳輸安全
6.4 監(jiān)控和運(yùn)維
- 集成 Prometheus + Grafana
- 配置合理的健康檢查
- 記錄詳細(xì)的訪問日志
這份文檔詳細(xì)說明了 Spring Cloud Gateway 的正確使用方式,特別強(qiáng)調(diào)了網(wǎng)關(guān)的職責(zé)邊界,避免了常見的架構(gòu)設(shè)計誤區(qū)。所有示例都包含詳細(xì)的中文注釋,幫助您理解每個組件的正確用途和實現(xiàn)方式。
到此這篇關(guān)于Spring Cloud Gateway詳細(xì)使用最佳實踐的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實現(xiàn)騰訊ocr圖片識別接口調(diào)用
這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)騰訊ocr圖片識別接口調(diào)用,拍車牌識別車牌號功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11
SpringCloud Eureka實現(xiàn)服務(wù)注冊與發(fā)現(xiàn)
Eureka是一種基于REST(具像狀態(tài)傳輸)的服務(wù),主要用于AWS云中定位服務(wù),以實現(xiàn)中間層服務(wù)器的負(fù)載平衡和故障轉(zhuǎn)移。本文記錄一個簡單的服務(wù)注冊與發(fā)現(xiàn)實例。感興趣的小伙伴們可以參考一下2019-01-01
使用@pathvariable與@requestparam碰到的一些問題及解決
這篇文章主要介紹了使用@pathvariable與@requestparam碰到的一些問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
對ArrayList和LinkedList底層實現(xiàn)原理詳解
今天小編就為大家分享一篇對ArrayList和LinkedList底層實現(xiàn)原理詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10
Spring占位符Placeholder的實現(xiàn)原理解析
這篇文章主要介紹了Spring占位符Placeholder的實現(xiàn)原理,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
Java數(shù)據(jù)庫存儲數(shù)組的方法小結(jié)
在現(xiàn)代軟件開發(fā)中,數(shù)組是常用的數(shù)據(jù)結(jié)構(gòu)之一,然而,在關(guān)系數(shù)據(jù)庫中直接存儲數(shù)組并不是一個簡單的任務(wù),本文將詳細(xì)介紹幾種在Java中將數(shù)組存儲到數(shù)據(jù)庫的方法,包括使用JPA、JSON、XML、以及關(guān)系型數(shù)據(jù)庫的數(shù)組類型等,需要的朋友可以參考下2024-09-09

