发布时间:2025-06-24 18:45:57  作者:北方职教升学中心  阅读量:333


实现动态路由功能,无需重启网关即可修改路由配置。考虑到微服务内部可能很多地方都需要用到登录用户信息,因此我们可以利用SpringMVC的拦截器来实现登录用户信息获取,并存入ThreadLocal,方便后续使用。

publicvoidaddListener(StringdataId,Stringgroup,Listenerlistener)

核心点:

  • 创建ConfigService,连接到Nacos(spring-cloud-starter-alibaba-nacos-config自动装配,拿到NacosConfigManager就等于拿到了ConfigService)
  • 添加配置监听器,编写配置变更的通知处理逻辑
StringserverAddr ="{serverAddr}";StringdataId ="{dataId}";Stringgroup ="{group}";// 1.创建ConfigService,连接NacosPropertiesproperties =newProperties();properties.put("serverAddr",serverAddr);ConfigServiceconfigService =NacosFactory.createConfigService(properties);// 2.读取配置Stringcontent =configService.getConfig(dataId,group,5000);// 3.添加配置监听器configService.addListener(dataId,group,newListener(){@OverridepublicvoidreceiveConfigInfo(StringconfigInfo){// 配置变更的通知处理System.out.println("recieve1:"+configInfo);}@OverridepublicExecutorgetExecutor(){returnnull;}});

更新路由表

packageorg.springframework.cloud.gateway.route;importreactor.core.publisher.Mono;/** * @author Spencer Gibb */publicinterfaceRouteDefinitionWriter{/**     * 更新路由到路由表,如果路由id重复,则会覆盖旧的路由     */Mono<Void>save(Mono<RouteDefinition>route);/**     * 根据路由id删除某个路由     */Mono<Void>delete(Mono<String>routeId);}

3.3.2 实现动态路由

1、

文章目录

  • 前言
  • 一、

    • 如何监听Nacos配置变更?
    • 如何把路由信息更新到路由表?

    3.3.1 监听nacos配置变更,更新路由表

    使用 Nacos 动态监听配置接口来实现。NacosDiscovery依赖

  • 编写启动类
  • 配置网关路由

1. 路由过滤

spring:  cloud:    gateway:      routes:        - id: item          uri: lb://item-service          predicates:            - Path=/items/**,/search/**   #请求路径必须符合指定规则

RouteDefinition就是具体的路由规则定义。

定义一个过滤器,在其中实现登录校验逻辑,并且将过滤器执行顺序定义到NettyRoutingFilter之前?

网关过滤器

  • GatewayFilter:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route.
  • GlobalFilter:全局过滤器,作用范围是所有路由,不可配置。怎么在转发之前做登录校验
    2、日志配置(logging)、
  • 最终把响应结果返回。在网关gateway的resources目录创建bootstrap.yaml文件

    spring:application:name:gateway  cloud:nacos:server-addr:192.168.150.101      config:file-extension:yaml        shared-configs:-dataId:shared-log.yaml # 共享日志配置

    3、SpringCloudGateway

    • 1. 路由过滤
    • 2. 网关登录校验
      • 2.1 鉴权
      • 2.2 网关过滤器
      • 2.3 登录校验
        • 2.3.1 JWT
        • 2.3.2 登录校验过滤器
    • 3. 微服务从网关获取用户
    • 4. 微服务之间用户信息传递
  • 三、
  • AddRequestHeaderGatewayFilterFacotry: 就是添加请求头的过滤器,可以给请求添加一个请求头并传递到下游微服务。

2. 网关登录校验

2.1 鉴权

在网关中保存密钥开发登录校验功能
在这里插入图片描述
问题
1、

  • 图中Filter被虚线分为左右两部分,是因为Filter内部的逻辑分为pre和post两部分,分别会在请求路由到微服务之前和之后被执行。网关路由

    数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发以及数据安全的校验。
    在这里插入图片描述

    3.1 配置共享

    3.1.1 在Nacos中添加共享配置

    抽取配置文件yaml中重复的模块,如jdbc相关配置(datasource、

  • WebHandler则会加载当前路由下需要执行的过滤器链(Filter chain),然后按照顺序逐一执行过滤器(后面称为Filter)。

    3.3 动态路由

    问题引入

    网关的路由配置全部是在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变,也不会监听路由变更。swagger(knife4j)以及OpenFeign的配置(feign)
    在这里插入图片描述
    在这里插入图片描述

    3.1.2 拉取共享配置

    如何去加载nacos中的配置文件?
    在这里插入图片描述
    实现步骤
    1、在gateway中定义配置监听器

    packagecom.hmall.gateway.route;importcn.hutool.json.JSONUtil;importcom.alibaba.cloud.nacos.NacosConfigManager;importcom.alibaba.nacos.api.config.listener.Listener;importcom.alibaba.nacos.api.exception.NacosException;importcom.hmall.common.utils.CollUtils;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.gateway.route.RouteDefinition;importorg.springframework.cloud.gateway.route.RouteDefinitionWriter;importorg.springframework.stereotype.Component;importreactor.core.publisher.Mono;importjavax.annotation.PostConstruct;importjava.util.HashSet;importjava.util.List;importjava.util.Set;importjava.util.concurrent.Executor;@Slf4j@Component@RequiredArgsConstructorpublicclassDynamicRouteLoader{privatefinalRouteDefinitionWriterwriter;privatefinalNacosConfigManagernacosConfigManager;// 路由配置文件的id和分组privatefinalStringdataId ="gateway-routes.json";privatefinalStringgroup ="DEFAULT_GROUP";// 保存更新过的路由idprivatefinalSet<String>routeIds =newHashSet<>();@PostConstructpublicvoidinitRouteConfigListener()throwsNacosException{// 1.注册监听器并首次拉取配置StringconfigInfo =nacosConfigManager.getConfigService().getConfigAndSignListener(dataId,group,5000,newListener(){@OverridepublicExecutorgetExecutor(){returnnull;}@OverridepublicvoidreceiveConfigInfo(StringconfigInfo){updateConfigInfo(configInfo);}});// 2.首次启动时,更新一次配置updateConfigInfo(configInfo);}privatevoidupdateConfigInfo(StringconfigInfo){log.debug("监听到路由配置变更,{}",configInfo);// 1.反序列化List<RouteDefinition>routeDefinitions =JSONUtil.toList(configInfo,RouteDefinition.class);// 2.更新前先清空旧路由// 2.1.清除旧路由for(StringrouteId :routeIds){writer.delete(Mono.just(routeId)).subscribe();}routeIds.clear();// 2.2.判断是否有新的路由要更新if(CollUtils.isEmpty(routeDefinitions)){// 无新路由配置,直接结束return;}// 3.更新路由routeDefinitions.forEach(routeDefinition ->{// 3.1.更新路由writer.save(Mono.just(routeDefinition)).subscribe();// 3.2.记录路由id,方便将来删除routeIds.add(routeDefinition.getId());});}}

    4、在模块中引入依赖

    <!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--读取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>

    2、新建一个属性读取类,在需要使用该属性的业务类中注入。自定义GlobalFilter
    定义一个类,直接实现GlobalFilter接口。
    微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。

    2.3 登录校验

    2.3.1 JWT
    • AuthProperties:配置登录校验需要拦截的路径,因为不是所有的路径都需要登录才能访问
    • JwtProperties:定义与JWT工具有关的属性,比如秘钥文件位置
    • SecurityConfig:工具的自动装配
    • JwtTool:JWT工具,其中包含了校验和解析token的功能
    • hmall.jks:秘钥文件
    2.3.2 登录校验过滤器

    定义一个登录校验的过滤器,实现GlobalFilter接口。新建bootstrap.yaml,配置nacos地址

    spring:cloud:nacos:server-addr:192.168.150.101 # nacos地址config:file-extension:yaml # 文件后缀名shared-configs:# 共享配置-dataId:shared-jdbc.yaml # 共享mybatis配置-dataId:shared-log.yaml # 共享日志配置-dataId:shared-swagger.yaml # 共享日志配置

    3.2 配置热更新

    问题引入

    业务相关参数,将来可能会根据实际情况临时调整。微服务之间怎么传递用户信息

    2.2 网关过滤器

    在这里插入图片描述
    实现原理

    1. 客户端请求进入网关后由HandlerMapping对请求做判断,找到与当前请求匹配的路由规则(Route),然后将请求交给WebHandler去处理。
    2. 只有所有Filter的pre逻辑都依次顺序执行通过后,请求才会被路由到微服务。自定义GatewayFilter
      定义一个类,这个类的名称一定要以GatewayFilterFactory为后缀,这个类继承了AbstractGatewayFilterFactory
      2、nacos配置管理
      • 问题引入
      • 3.1 配置共享
        • 3.1.1 在Nacos中添加共享配置
        • 3.1.2 拉取共享配置
      • 3.2 配置热更新
        • 问题引入
        • 实现步骤
      • 3.3 动态路由
        • 问题引入
        • 3.3.1 监听nacos配置变更,更新路由表
        • 3.3.2 实现动态路由

  • 前言

    前端请求不能直接访问微服务,而是要请求网关(SpringCloudGateway)
    网关干什么?路由过滤,登录校验。mybatis-plus)、
    nacos既是注册中心也可用作配置管理,用来解决各个微服务块配置文件中相同的配置冗余,配置热更新属性,动态路由等。校验JWT之后,怎么把用户信息传递给微服务
    3、
    在这里插入图片描述
    实现步骤:

    • 改造网关过滤器,在获取用户信息后保存到请求头,转发到下游微服务
    • 编写微服务拦截器,拦截请求获取用户信息,保存到ThreadLocal后放行(因为每个微服务都执行获取用户信息的逻辑,因此统一拦截所有请求,进行处理(AOP的思想))

    4. 微服务之间用户信息传递

    由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头。
    在这里插入图片描述
    微服务中的网关作用:前端请求不能直接访问微服务,而是要请求网关。网关gateway引入依赖

    <!--统一配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--加载bootstrap--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>

    2、直接在yaml文件中配置

    1、

    一、

    • id:路由的唯一标示
    • predicates:路由断言,其实就是匹配条件
    • filters:路由过滤条件
    • uri:路由目标地址,lb://代表负载均衡,从注册中心获取目标微服务的实例列表,并且负载均衡选择一个访问。实现步骤:

      • 创建网关微服务模块
      • 引入SpringCloudGateway、

        • 网关可以做安全控制,也就是登录身份校验,校验通过才放行
        • 通过认证后,网关再根据请求判断应该访问哪个微服务,将请求转发过去

        二、

      • 微服务返回结果后,再倒序执行Filter的post逻辑。FilteringWebHandler在处理请求时,会将GlobalFilter装饰为GatewayFilter,然后放到同一个过滤器链中,排序以后依次执行。在nacos中配置路由信息,路由文件名为gateway-routes.json,类型为json

        [{"id":"item","predicates":[{"name":"Path","args":{"_genkey_0":"/items/**","_genkey_1":"/search/**"}}],"filters":[],"uri":"lb://item-service"},....

    @Component@RequiredArgsConstructor@EnableConfigurationProperties(AuthProperties.class)publicclassAuthGlobalFilterimplementsGlobalFilter,Ordered{privatefinalJwtTooljwtTool;privatefinalAuthPropertiesauthProperties;privatefinalAntPathMatcherantPathMatcher =newAntPathMatcher();@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){// 1.获取RequestServerHttpRequestrequest =exchange.getRequest();// 2.判断是否不需要拦截if(isExclude(request.getPath().toString())){// 无需拦截,直接放行returnchain.filter(exchange);}// 3.获取请求头中的tokenStringtoken =null;List<String>headers =request.getHeaders().get("authorization");if(!CollUtils.isEmpty(headers)){token =headers.get(0);}// 4.校验并解析tokenLonguserId =null;try{userId =jwtTool.parseToken(token);}catch(UnauthorizedExceptione){// 如果无效,拦截ServerHttpResponseresponse =exchange.getResponse();response.setRawStatusCode(401);returnresponse.setComplete();}// TODO 5.如果有效,传递用户信息//保存用户请求头StringuserInfo =userId.toString();ServerWebExchangeex =exchange.mutate().request(b ->b.header("user-info",userInfo)).build();// 6.放行returnchain.filter(exchange);}privatebooleanisExclude(StringantPath){for(StringpathPattern :authProperties.getExcludePaths()){if(antPathMatcher.match(pathPattern,antPath)){returntrue;}}returnfalse;}@OverridepublicintgetOrder(){return0;}}

    3. 微服务从网关获取用户

    将用户信息以请求头的方式传递到下游微服务,然后微服务可以从请求头中获取登录用户信息。nacos配置管理

    问题引入

    • 网关路由在配置文件中写死了,如果变更必须重启微服务
    • 某些业务配置在配置文件中写死了,每次修改都要重启服务
    • 每个微服务都有很多重复的配置,维护成本高

    nacos: 无需重启即可生效,实现配置热更新。在nacos中添加配置属性
    2、我们如何才能让每一个由OpenFeign发起的请求自动携带登录用户信息呢?
    Feign中提供的一个拦截器接口:feign.RequestInterceptor
    实现方式
    定义一个类,实现RequestInterceptor接口,实现接口中的apply方法,用RequestTemplate类来添加请求头,将用户信息保存到请求头中。网关路由

  • 二、

    实现步骤

    1、

    @BeanpublicRequestInterceptoruserInfoRequestInterceptor(){returnnewRequestInterceptor(){@Overridepublicvoidapply(RequestTemplatetemplate){// 获取登录用户LonguserId =UserContext.getUser();if(userId ==null){// 如果为空则直接跳过return;}// 如果不为空则放入请求头中,传递给下游微服务template.header("user-info",userId.toString());}};}

    在这里插入图片描述

    三、SpringCloudGateway

    网关本身也是一个独立的微服务,所以也需要单独建立一个模块。例如购物车业务,购物车数量有一个上限,默认是10。