微服务——网关、网关登录校验、OpenFeign传递共享信息、Nacos共享配置以及热更新、动态路由
之前学习了Nacos,用于发现并注册、管理项目里所有的微服务,而OpenFeign简化微服务之间的通信,而为了使得前端可以使用微服务项目里的每一个微服务的接口,就应该将所有微服务的接口管理起来方便前端调用,所以有了网关。
前端调用后端微服务项目的接口时,不需要指定每一个接口具体的地址,只需要将请求发送到后端的网关即可。
网关介绍
网关是网络的关口,负责请求的路由、转发、身份校验 。

网关模块的配置
1、新建一个maven空模块,配置一下依赖
<dependencies>
......<!-- 其它依赖 --><!--网关-->
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency> <!--nocos 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>
<build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins>
</build>
2、创建com.<XXX项目名称>.gateway包,报下名新建配置类
@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
3、在静态资源目录下新建application.yaml文件,配置网关相关属性
server:port: 8080 # 网关服务的端口号,指定网关运行在 8080 端口
spring:application:name: gateway # 应用名称,注册到 Nacos 的服务名称cloud:nacos:server-addr: 192.168.52.128:8848 # Nacos 服务器地址,配置 Nacos 注册中心地址gateway:routes: # 路由配置- id: item-service # 路由 ID,唯一标识,可以随便命名uri: lb://item-service # 目标服务地址,即从注册中心获取 item-service 的地址predicates: # 断言,即路由转发的规则- Path=/items/**,/search/** # 匹配 /items/ 开头的和 /search/ 开头的请求到 item-service 服务获取响应- id: user-service uri: lb://user-service predicates: - Path=/items/**,/search/**
4、最后启动整个项目的时候也要把网关启动
由下图可见网关的效果有了

网关的登录校验

网关过滤器有两种,分别是:
- GatewayFilter: 路由过滤器,作用于任意指定的路由;默认不生效,要配置到路由后生效。
- GlobalFilter: 全局过滤器,作用范围是所有路由;声明后自动生效。
网关加公共依赖XXX-common实现请求的校验
1、网关过滤器过滤请求(Filters文件夹)
@Component // 将该类标记为Spring组件,使其成为Spring容器管理的Bean
@RequiredArgsConstructor // Lombok注解,自动生成一个包含所有final字段的构造函数
public class AuthGlobalFilter implements GlobalFilter, Ordered {// 依赖注入JwtTool,用于JWT的解析和验证private final JwtTool jwtTool;// 依赖注入AuthProperties,包含认证相关的配置信息,如排除路径等private final AuthProperties authProperties;// AntPathMatcher用于路径匹配,判断请求路径是否在排除路径中private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1. 获取当前请求对象ServerHttpRequest request = exchange.getRequest();// 2. 判断请求路径是否需要登录拦截if (isExclude(request.getPath().toString())) {// 如果路径在排除列表中,直接放行,不进行拦截return chain.filter(exchange);}// 3. 从请求头中获取tokenString token = null;List<String> headers = request.getHeaders().get("authorization");if (headers != null && !headers.isEmpty()) {token = headers.get(0); // 获取第一个authorization头,通常为Bearer Token}// 4. 校验并解析tokenLong userId = null;try {// 使用JwtTool解析token,获取用户IDuserId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果token无效或解析失败,拦截请求并返回401 Unauthorized状态码ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete(); // 结束请求处理}// 打印用户ID(通常用于调试,生产环境中不建议直接打印敏感信息)System.out.println("userId = " + userId);String userInfo = userId.toString();// 将用户信息存入请求头ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info", userInfo)).build();// 5. 如果token有效,继续执行后续的过滤器链return chain.filter(swe);}// 判断请求路径是否在排除路径列表中private boolean isExclude(String path) {for (String pathPattern : authProperties.getExcludePaths()) {// 使用AntPathMatcher进行路径匹配if (antPathMatcher.match(pathPattern, path)) {return true; // 如果匹配到排除路径,返回true}}return false; // 否则返回false}@Overridepublic int getOrder() {// 返回过滤器的执行顺序,0表示最高优先级return 0;}
}
过滤器里涉及的一些依赖
// jwt校验工具
@Component
public class JwtTool {private final JWTSigner jwtSigner;public JwtTool(KeyPair keyPair) {this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);}public String createToken(Long userId, Duration ttl) {// 1.生成jwsreturn JWT.create().setPayload("user", userId).setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis())).setSigner(jwtSigner).sign();}/*** 解析token** @param token token* @return 解析刷新token得到的用户信息*/public Long parseToken(String token) {// 1.校验token是否为空if (token == null) {throw new UnauthorizedException("未登录");}// 2.校验并解析jwtJWT jwt;try {jwt = JWT.of(token).setSigner(jwtSigner);} catch (Exception e) {throw new UnauthorizedException("无效的token", e);}// 2.校验jwt是否有效if (!jwt.verify()) {// 验证失败throw new UnauthorizedException("无效的token");}// 3.校验是否过期try {JWTValidator.of(jwt).validateDate();} catch (ValidateException e) {throw new UnauthorizedException("token已经过期");}// 4.数据格式校验Object userPayload = jwt.getPayload("user");if (userPayload == null) {// 数据为空throw new UnauthorizedException("无效的token");}// 5.数据解析try {return Long.valueOf(userPayload.toString());} catch (RuntimeException e) {// 数据格式有误throw new UnauthorizedException("无效的token");}}
}// 拦截器拦截
@Data
@Component
@ConfigurationProperties(prefix = "hm.auth")
public class AuthProperties {private List<String> includePaths;private List<String> excludePaths;
}
2、网关的yaml文件里配置不需要校验直接放行的请求
hm:jwt: #解析jwt密钥文件location: classpath:hmall.jksalias: hmallpassword: hmall123tokenTTL: 30mauth:excludePaths:- /search/**- /users/login- /items/**- /hi
3、由于每一个微服务都导入了XX-common模块的依赖,所以在XX-common模块里配置并注册拦截器,拦截所有发送到每个微服务里的请求,用于将请求头里用户信息存入线程池。
public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取登录用户信息String userInfo = request.getHeader("user-info"); // 从请求头里获取// 2. 判断是否获取了用户,如果有,存入ThreadLocalif (StrUtil.isNotBlank(userInfo)) {UserContext.setUser(Long.valueOf(userInfo));}// 3. 放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理用户UserContext.removeUser();}
}
4、注册XX-common模块里的拦截器
@Configuration
@ConditionalOnClass(DispatcherServlet.class) // 使得网关不去生效改拦截器
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());// 默认拦截所有的请求,目的是为了将每一个请求里包含的用户信息存入线程池}
}
5、配置静态资源文件夹下的spring.factories文件,取保每个微服务可以读取到XX-common模块里的拦截器
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig,\com.hmall.common.config.JsonConfig
OpenFeign传递用户信息
使用OpenFeign时,一个微服务发送给另一个微服务的请求也要携带用户信息到请求头里,要和网关发送给微服务的请求一样。所有要在公共api模块里加拦截器,使得每一个请求的请求头里添加用户信息。
写到OpenFeign的配置类里,且微服务的启动类加上@EnableFeignClients(basePackages = "com.hmall.api.client", defaultConfiguration = DefaultFeignConfig.class)的注解
// 写到OpenFeign的配置类里,且微服务的启动类加上
// @EnableFeignClients(basePackages = "com.hmall.api.client",
// defaultConfiguration = DefaultFeignConfig.class)的注解
@Bean // 声明为一个Bean,可以被Spring容器管理public RequestInterceptor userInfoRequestInterceptor() {return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 获取当前用户的IDLong userId = UserContext.getUser(); // 导入XXX-common模块里的线程池// 如果用户ID不为空,则添加到请求头中if (userId != null) { // 确保每一个微服务之间发送的请求也携带user-info到请求头里// 将用户ID添加到请求头中,key为"user-info"System.out.println("将用户ID添加到请求头中,key为user-info,id为" + userId);template.header("user-info", userId.toString());}}};}

nacos共享配置
由于每一个微服务的yaml文件里有多个共同的配置信息,所有可以将其抽取出来的配置共同使用nacos注册中心配置。

每一个微服务里导入如下依赖即可实现。
<!-- 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>
nacos里共同配置的信息()${XXX:YY}表示如果读取不到XXX则默认为YY
# 数据库和mybatis
spring:datasource:url: jdbc:mysql://${hm.db.host:192.168.52.128}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: ${hm.db.un:root}password: ${hm.db.pw:123}
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal-config:db-config:update-strategy: not_nullid-type: auto# 日志记录
logging:level:com.hmall: debugpattern:dateformat: HH:mm:ss:SSSfile:path: "logs/${spring.application.name}"# swagger配置
knife4j:enable: trueopenapi:title: ${hm.swagger.title:黑马商城接口文档}description: ${hm.swagger.desc:黑马商城接口文档}email: zhanghuyi@itcast.cnconcat: 虎哥url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- ${hm.swagger.package}
拉取nacos里的配置文件到本地微服务(以下为bootstrap.yaml文件)
spring: main: additional-properties: --add-opens=java.base/java.lang.invoke=ALL-UNNAMED application: # 应用程序名称,用于标识该服务,在Nacos或其他服务注册中心中可用到 name: cart-service cloud: nacos: # Nacos的服务地址,用于连接到Nacos服务器 server-addr: localhost:8848 # nacos地址 config: # 配置文件的格式,这里指定为YAML格式 file-extension: yaml # 定义共享配置文件列表,这些配置将从Nacos服务器加载 shared-configs: # 一定对应好nacos里的Data ID- data-id: shared-jdbc.yaml # JDBC共享配置文件 - data-id: shared-log.yaml # 日志共享配置文件 - data-id: shared-swagger.yaml # Swagger共享配置文件
nacos配置里的变量在本地微服务里配置好(以下为application.yaml文件)
server:port: 8082
feign:okhttp:enabled: true
hm:db:database: hm-cartswagger:title: "黑马城市购物车服务接口文档"package: com.hmall.cart.controller
配置热更新
配置热更新:修改配置文件里的配置的时候,不需要重新启动微服务项目配置就可以生效配置。
具体应用实例
需求:购买车的限定数量目前是写死在业务中的,将其改为读取配置文件属性,并将配置置交给Nacos管理,实现热更新。

首先在nocas配置要限定数量所在的微服务的yaml文件

之后在对应的微服务里添加config文件
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart") // 对应yaml文件里的配置
public class CartProperties {private Integer maxItems;
}
最后在业务文件里面就可以去使用了
private final CartProperties cartProperties; // 导入依赖
......private void checkCartsFull(Long userId) {int count = lambdaQuery().eq(Cart::getUserId, userId).count();if (count >= cartProperties.getMaxItems()) {throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", cartProperties.getMaxItems()));}}
......相关文章:
微服务——网关、网关登录校验、OpenFeign传递共享信息、Nacos共享配置以及热更新、动态路由
之前学习了Nacos,用于发现并注册、管理项目里所有的微服务,而OpenFeign简化微服务之间的通信,而为了使得前端可以使用微服务项目里的每一个微服务的接口,就应该将所有微服务的接口管理起来方便前端调用,所以有了网关。…...
【leetcode hot 100 19】删除链表的第N个节点
解法一:将ListNode放入ArrayList中,要删除的元素为num list.size()-n。如果num 0则将头节点删除;否则利用num-1个元素的next删除第num个元素。 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...
comctl32!ListView_OnSetItem函数分析LISTSUBITEM结构中的image表示图标位置
第一部分: BOOL ListView_SetSubItem(LV* plv, const LV_ITEM* plvi) { LISTSUBITEM lsi; BOOL fChanged FALSE; int i; int idpa; HDPA hdpa; if (plvi->mask & ~(LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) { …...
数据结构——多项式问题(顺序存储结构or链式存储结构)
补充:malloc函数: malloc 函数是 C 语言标准库中的一个重要函数,位于 <stdlib.h> 头文件中,主要用于在程序运行时动态分配内存。以下将详细介绍其用法。 前面的返回值指针可以自己定义,如 (int*&am…...
【学习方法】技术开发者的提问智慧:如何高效获得解答?
技术开发者的提问智慧:如何高效获得解答? 在技术开发过程中,每个人都会遇到无法解决的问题。此时,我们通常会向团队、社区或论坛求助。然而,为什么有些人的问题能迅速得到解答,而有些人的问题却石沉大海&a…...
记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)
文章目录 记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)一、微信小程序注册摘要1.1 注册流程要点 二、小程序发布流程三、下载工具 记录小白使用 Cursor 开发第一个微信小程序(…...
vue2项目修改浏览器显示的网页图标
1.准备一个新的图标文件,通常是. ico格式,也可以是. Png、. Svg等格式 2.将新的图标文件(例如:faviconAt.png)放入项目的public文件夹中。如下图 public文件夹中的所有文件都会在构建时原样复制到最终的输出目录(通常是dist) 3. 修改vue项目…...
spring boot3.4.3+MybatisPlus3.5.5+swagger-ui2.7.0
使用 MyBatis-Plus 操作 books 表。我们将实现以下功能: 创建实体类 Book。 创建 Mapper 接口 BookMapper。 创建 Service 层 BookService 和 BookServiceImpl。 创建 Controller 层 BookController。 配置 MyBatis-Plus 和数据库连接。 1. 项目结构 src ├─…...
【网络安全工程】任务10:三层交换机配置
CSDN 原创主页:不羁https://blog.csdn.net/2303_76492156?typeblog三层交换机是指在OSI(开放系统互连)模型中的第三层网络层提供路由功能的交换机。它不仅具备二层交换机的交换功能,还能实现路由功能,提供更为灵活的网…...
侯捷 C++ 课程学习笔记:C++内存管理机制
内存管理从平地到万丈高楼 内存管理入门(Memory Management 101) 需要具有动态分配并使用memory(存储(器),(计算机的)内存),使用过C标准库的容器࿰…...
JVM常用概念之本地内存跟踪
问题 Java应用启动或者运行过程中报“内存不足!”,我们该怎么办? 基础知识 对于一个在本地机器运行的JVM应用而言,需要足够的内存来存储机器代码、堆元数据、类元数据、内存分析等数据结构,来保证JVM应用的成功启动以及未来平…...
【鸿蒙开发】Hi3861学习笔记- 软件定时器示例
00. 目录 文章目录 00. 目录01. 定时器概述02. 定时器API03. 定时器常用API3.1 osTimerNew3.2 osTimerDelete3.3 osTimerStart3.4 osTimerStop 04. 程序示例05. 附录 01. 定时器概述 软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当经过设…...
在Html5中仿Matlab自定义色带生成实践
目录 前言 一、RGB的相关知识 1、RGB的基本原理 2、RGB的数值表示 3、应用场景 二、ColorMap生成实战 1、外部库介绍 2、相关API 3、实例生成 三、总结 前言 在现代网页开发与数据可视化领域,色彩的表现力对于信息传达和视觉体验起着至关重要的作用。色带&…...
贪心算法--
1.柠檬水找零 link:860. 柠檬水找零 - 力扣(LeetCode) code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法, 优先花出大面额bill, 尽可能保护小面额billint five 0, ten 0;// 不…...
【学习方法一】
学习方法一 一、通用高效学习法二、学科专项方法三、工具与技术辅助四、习惯与心理策略五、避免常见误区总结六、进阶学习策略七、解决学习痛点八、场景化学习法九、资源与工具推荐十、个性化学习调整十一、长期学习心态十二、常见问题QA十三、应对特殊挑战的学习法十四、健康与…...
k8s启动时calico-kube-controllers与coredns组件一直是pending状态
症状: k8s执行kubectl get po -n kube-system时,以下组件一直>是pending状态: calico-kube-controllerscoredns 当执行 kubectl get po -n kube-system 发现 calico-kube-controllers 和 coredns 一直处于 Pending 状态时,通常…...
ngx_openssl_create_conf
ngx_openssl_create_conf 声明在 src\event\ngx_event_openssl.c static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); 定义在 src\event\ngx_event_openssl.c static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) {ngx_openssl_conf_t *oscf;oscf ngx_…...
如何选择国产串口屏?
目录 1、迪文 2、淘晶驰 3、广州大彩 4、金玺智控 5、欣瑞达 6、富莱新 7、冠显 8、有彩 串口屏,顾名思义,就是通过串口通信接口(如RS232、RS485、TTL UART等)与主控设备进行通信的显示屏。其核心功能是显示信息和接收输入…...
matlab慕课学习3.1
3.1顺序结构程序 于20250306 3.1.1程序和程序设计 程序是用某种计算机能够理解并且能够执行的语言来描述的解决问题的方法和步骤。 3.1.2程序的三种基本结构 1.顺序结构 2.选择结构 3.循环结构 3.1.3脚本文件和函数文件 脚本文件是可在命令行窗口直接执行的文件࿰…...
EB-Cable许可管理系统的功能和特点
在数字化时代,软件许可管理已成为企业日常运营中不可或缺的一部分。EB-Cable许可管理系统作为一款专为电缆管理而设计的软件解决方案,为企业提供了全面、高效且灵活的许可管理功能。本文将详细介绍EB-Cable许可管理系统的功能和特点,帮助您快…...
cesium地图设置3d,2d,2.5d动态切换
通过修改cesium实例vw的scene的显示模式,来切换最终的显示模式。 Cesium.SceneMode总共有四个变量值,分别如下:NameTypeDescriptionMORPHINGnumber在3d与2d之间切换变体 between mode, e.g., 3D to 2D.COLUMBUS_VIEWnumber2.5d模式࿰…...
Mac如何查看 IDEA 的日志文件
在 macOS 上,IntelliJ IDEA 的日志文件通常存储在用户目录下的 .IntelliJIdea<版本号> 文件夹中。以下是查看日志文件的具体步骤: 1. 找到日志文件的位置 日志文件通常位于以下路径: ~/Library/Logs/IntelliJIdea<版本号> 其…...
linux 软件安装(下)
七、ElasticSearch安装 官网地址:Elasticsearch:官方分布式搜索和分析引擎 | Elastic 官网下载地址:Past Releases of Elastic Stack Software | Elastic 7.1、linux安装 1、上传安装包 altp # 打开sftp窗口 # 上传es安装包 put e:/sof…...
MongoDB 自动化部署
部署在容器中,并且自动创建所需用户和权限等 # 启动 mongoDBsudo docker run -dit --name china_fish_mongo \ -p 27017:27017 \ -v /data/project1/db/mongo/config/mongod.conf:/etc/mongod.conf \ -v /data/project1/db/mongo/data:/data/db \ -v /data/project1…...
程序化广告知识入门与Python基础数据处理实践
程序化广告知识入门与Python基础数据处理实践 大家好!我写这一系列博客的初衷是想和大家一起学习进步。在技术飞速发展的今天,数据处理能力愈发重要,Python作为强大的数据处理工具,掌握它能为我们的职业发展和技术提升带来极大帮…...
【数据结构】二叉搜索树、平衡搜索树、红黑树
二叉搜索树(Binary Search Tree) 二叉搜索树是一种特殊的二叉树,它用来快速搜索某个值,对于每个节点都应该满足以下条件: 若该节点有左子树,那么左子树中所有节点的值都应该小于该节点的值。若该节点有右…...
密码学(终极版)
加密 & 解密 备注:密码学领域不存在完全不能破解的密码,但是如果一个密码需要很久很久,例如一万年才能破解,就认为这个密码是安全的了。 对称加密 非对称加密 公钥加密、私钥解密 私钥签名、公钥认证 非对称的底层原理是…...
经销商管理系统选型解析:8款产品详评
本文主要介绍了以下8款经销商管理系统:1.纷享销客; 2.用友T6经销商管理系统; 3.金蝶经销商管理系统; 4.鼎捷经销商管理系统; 5.浪潮经销商管理系统; 6.销售易; 7.SAP Business One Distributor …...
Go 语言封装 HTTP 请求的 Curl 工具包
文章目录 Go 语言封装 HTTP 请求的 Curl 工具包🏗️ 工具包结构简介核心结构体定义初始化函数 🌟 功能实现1. 设置请求头2. 构建请求3. 发送请求4. 发送 GET 请求5. 发送 POST 请求6. 发送 PUT 请求7. 发送 DELETE 请求8. 读取响应体 💡 实现…...
【C++】函数重载与nullptr
1、函数重载 C支持在同一个作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是形参个数不同或者类型不同。这样C函数调用就表现出了多态行为,使用更灵活。C语言是不支持同一作用域中出现同名函数的。 代码: 形参类型不…...
