目录
[2.3.1 请求头接收](#2.3.1 请求头接收)
[2.3.2 在公共类编写拦截器](#2.3.2 在公共类编写拦截器)
[2.4 OpenFeign传递用户](#2.4 OpenFeign传递用户)
[3.1.1 添加配置到Nacos](#3.1.1 添加配置到Nacos)
[3.1.2 拉取共享配置](#3.1.2 拉取共享配置)
[3.2 配置热更新](#3.2 配置热更新)
[3.3 动态路由](#3.3 动态路由)
1.网关简述

客户端 只认网关地址
http://localhost:8080/items后面有多少微服务、换不换 IP 它都不用知道。
网关 收到请求后:
去 注册中心 拉取当前可用的 item-service 实例列表(服务发现)
做 身份校验(JWT/登录态/签名)
内置 负载均衡器 挑一台健康实例
路由转发 把请求发过去
微服务 启动时会把自身地址 注册 到注册中心,并持续 心跳 保活;
挂掉或网络抖动时注册中心会把它踢出列表,网关就不会再转发流量给它。

| 维度 | Netflix Zuul | Spring Cloud Gateway |
|---|---|---|
| 出身 | Netflix 老牌套件 | Spring 官方继任者 |
| 技术栈 | 基于 Servlet 2.x,阻塞式(BIO) | 基于 WebFlux + Netty,响应式(NIO) |
| 性能 | 需要调优才能追平 Gateway | 不调优也原生高吞吐、低延迟 |
| 维护状态 | Netflix 已停止维护 Zuul 1,Spring Cloud 2020 起移除 | 持续迭代,官方主推 |



2.网关登陆校验
2.1自定义过滤器
自定义登录拦截器:实现全局过滤器接口GlobalFilter 和 排序接口Ordered

"从
exchange拿请求 → 做业务 →chain.filter(exchange)把 同一个exchange交给下一个过滤器"。
在 Spring Cloud Gateway 里,没有传统的 HttpServletRequest / HttpServletResponse ,而是把一次 HTTP 交互(请求 + 响应 + 上下文)整体封装成一个
ServerWebExchange。
ServerWebExchange exchange
exchange.getRequest()→ 得到ServerHttpRequest(URI、Header、Body)
exchange.getResponse()→ 得到ServerHttpResponse(Status、Header、Body)
exchange.getAttributes()→ 自定义 KV 上下文(放 traceId、用户ID 等)一句话:大容器里装着"请求对象 + 响应对象 + 公共行李"。
GatewayFilterChain chain责任链模式,每个过滤器必须调
chain.filter(exchange)才能把"包裹"交给下一个;不写这行 = 请求在此中断,网关直接返回当前响应(常用于鉴权失败、限流拒绝)
"下一个"是谁 ≠ 只看你自己的
getOrder(),而是看整个网关里 所有过滤器的getOrder()值从小到大排好的顺序表 。Spring Cloud Gateway 启动时会一次性把 全局过滤器 按
order升序排成一条链,你调用chain.filter(exchange)只是 把指针往后移一位,并不会动态挑过滤器。
2.2实现登录校验
java
@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final JwtTool jwtTool;
private final AuthProperties authProperties;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取Request
ServerHttpRequest request = exchange.getRequest();
// 2.判断是否不需要拦截
if(isExclude(request.getPath().toString())){
// 无需拦截,直接放行
return chain.filter(exchange);
}
// 3.获取请求头中的token
String token = null;
List<String> headers = request.getHeaders().get("authorization");
if (!CollUtils.isEmpty(headers)) {
token = headers.get(0);
}
// 4.校验并解析token
Long userId = null;
try {
userId = jwtTool.parseToken(token);
} catch (UnauthorizedException e) {
// 如果无效,拦截
ServerHttpResponse response = exchange.getResponse();
response.setRawStatusCode(401);
return response.setComplete();
}
//5.传递用户信息
String userInfo= userId.toString();
ServerWebExchange ex=exchange.mutate()
.request(b->b.header("user", userInfo))
.build();
// 6.放行
return chain.filter(ex);
}
private boolean isExclude(String antPath) {
// 如果排除路径列表为空,直接返回false
if (authProperties.getExcludePaths() == null || authProperties.getExcludePaths().isEmpty()) {
return false;
}
for (String pathPattern : authProperties.getExcludePaths()) {
if (pathPattern == null || pathPattern.isEmpty()) {
continue;
}
// 使用 AntPathMatcher 进行路径匹配
if(antPathMatcher.match(pathPattern, antPath)){
return true;
}
}
return false;
}
@Override
public int getOrder() {
// 设置较高的优先级,确保在CORS过滤器之后执行
return 0;
}
}
1.获取request
2.判断是否需要做登录拦截
3.获取token
4.校验并解析token
5.传递用户信息
6.放行
2.3网关传递用户
在网关层中,将token解析完后写入请求头。
此时网关对微服务发送请求时,请求头携带了用户信息。
2.3.1 请求头接收


网关使用 exchange.mutate() 创建一个新的 ServerWebExchange 实例
通过 request(b -> b.header("user", userInfo)) 在请求头中添加一个名为 "user" 的字段
将用户ID字符串作为该请求头的值
使用 @RequestHeader(value="user", required=false) 注解标记参数value="user" 指定从请求头中名为 "user" 的字段获取值
required=false 表示该请求头不是必需的
2.3.2 在公共类编写拦截器
定义拦截器
javapublic class UserInfoInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1.获取请求头中的用户信息 String userInfo = request.getHeader("user-info"); // 2.判断是否为空 if (StrUtil.isNotBlank(userInfo)) { // 不为空,保存到ThreadLocal UserContext.setUser(Long.valueOf(userInfo)); } // 3.放行 return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 移除用户 UserContext.removeUser(); } }实现了 preHandle 方法用于在请求前设置用户信息到 UserContext
实现了 afterCompletion 方法用于在请求完成后清理 UserContext 中的用户信息
配置拦截器
java@Configuration @ConditionalOnClass(DispatcherServlet.class) public class MvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInfoInterceptor()); } }1. InterceptorRegistry
Spring MVC 的拦截器注册中心
提供链式 API 配置拦截器
底层维护一个拦截器列表,按注册顺序执行
2. addInterceptor() 方法
当前实现的局限性:
// 当前实现 - 简单但不够完善 registry.addInterceptor(new UserInfoInterceptor());完整配置应该包含:
registry.addInterceptor(new UserInfoInterceptor()) .addPathPatterns("/**") // 拦截所有路径 .excludePathPatterns( // 排除不需要拦截的路径 "/auth/login", "/public/**", "/swagger-ui/**", "/v3/api-docs/**", "/error" ) .order(1); // 设置执行顺序
自动扫描配置
javaorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.hmall.common.config.MyBatisConfig,\ com.hmall.common.config.MvcConfig,\ com.hmall.common.config.JsonConfig
组件 作用 类比 MvcConfig 定义拦截器的行为 菜谱:怎么做菜 spring.factories 告诉Spring Boot加载这个配置 菜单:有哪些菜可用 @ConditionalOnClass 决定是否真的执行配置 食材检查:有食材才做菜
1. spring.factories 告诉 Spring Boot:"我有这些自动配置类" 2. Spring Boot 检查条件注解(如 @ConditionalOnClass) 3. 条件满足 → 实例化 MvcConfig 4. MvcConfig.addInterceptors() 被调用 5. 拦截器注册到 Spring MVC没有 spring.factories :再好的配置也不会被加载 → 拦截器不生效
没有 @ConditionalOnClass :可能在不支持 Web 的环境报错
两者结合:实现"智能的、条件化的自动配置"
2.4 OpenFeign传递用户


网关统一登录
用户只在 API Gateway 做一次登录(JWT/Session),网关解析出 userId。
下游服务无需再次鉴权
业务服务(cart、order...)不直连登录中心 ,只要知道"当前是谁"即可。
→ 必须把 userId 从网关带到每一个服务。
Feign 调用同样需要用户
服务 A → 服务 B 的 OpenFeign 调用也要带用户,否则会丢失上下文。
线程安全 & 可清理
一次请求可能在 Tomcat 线程池里切换,需要线程级变量,并在结束后立即清理,防止内存泄漏。

3.配置管理

3.1配置共享
3.1.1 添加配置到Nacos

3.1.2 拉取共享配置

由于启动时拉去Nacos配置比读取application.yml文件早,所以对于Nacos地址的配置放在
bootstrap.yml文件中。


3.2 配置热更新

启动会自动根据 微服务名称+项目profile(可选)+文件后缀 加载Nacos中相关配置



