Gateway:网关路由与登录鉴权
在微服务架构中,用户登录和身份校验的处理方式确实与单体应用有所不同。在单体架构中,一旦用户通过身份验证,其会话信息可以在整个应用范围内共享,所有模块都能访问到用户信息。然而,在微服务架构下,每个服务独立部署且通常运行在不同的进程中,因此需要一种机制来确保用户的身份信息能够在各个微服务之间安全、高效地传递和验证。
目录
网关实现路由
网关登录鉴权
鉴权思路
登录校验流程图
网关过滤器详解
实现网关过滤器
拦截器流程图
服务信息鉴权
网关实现路由
问题:每个微服务都有不同的地址或端口,入口不同,请求不同数据时要访问不同的入口,需要维护多个入口地址,前端无法调用nacos,无法实时更新服务列表。
解决方案:采用微服务网关,数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发。
在微服务中新建一个网关模块,作为网关微服务
引入依赖(需要加入nacos依赖,让nacos管理)
<dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--网关--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--nacos 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>
在application.yaml
文件,配置路由
gateway:routes:- id: item # 路由规则id,自定义,唯一uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务- Path=/items/**,/search/** # 这里是以请求路径作为判断规则
经测试成功
网关登录鉴权
问题:单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息。但是,在微服务中,每个微服务都独立部署,一般只有用户微服务能校验登录信息,事实上,这是不安全的。
解决方案:既然网关是所有微服务的入口,一切请求都需要先经过网关。我们完全可以把登录校验的工作放到网关去做。
鉴权思路
登录是基于JWT来实现的,校验JWT的算法复杂,而且需要用到秘钥。我们不可能让个微服务都需要知道JWT的秘钥,不安全。也不可能每个微服务重复编写登录校验代码、权限校验代码,麻烦。
所以,我们在网关和用户服务保存秘钥,开发登录校验功能。
登录校验流程图
我们可以看到:前端——网关——后端服务
我们在网关层去实现过滤请求。
网关过滤器详解
Gateway
内部工作的基本原理:
如图所示:
-
客户端请求进入网关后由
HandlerMapping
对请求做判断,找到与当前请求匹配的路由规则(Route
),然后将请求交给WebHandler
去处理 -
WebHandler
则会加载当前路由下需要执行的过滤器链(Filter chain
),然后按照顺序逐一执行过滤器(后面称为Filter
) -
图中
Filter
被虚线分为左右两部分,是因为Filter
内部的逻辑分为pre
和post
两部分,分别会在请求路由到微服务之前和之后被执行 -
只有所有
Filter
的pre
逻辑都依次顺序执行通过后,请求才会被路由到微服务 -
微服务返回结果后,再倒序执行
Filter
的post
逻辑 -
最终把响应结果返回
反正就是:我们需要在NettyRoutingFilter过滤器之前,在发起Request时,即pre时,定义一个过滤器,进行网关登录校验。
实现网关过滤器
我们采用全局过滤器,作用范围是所有路由,即GlobalFilter。
package com.hmall.gateway.filters;import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
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.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@RequiredArgsConstructor
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();/*** 过滤器* @param exchange* @param chain* @return*/@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);}// 4.解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果无效,拦截ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}// 5.获取用户信息String userInfo = userId.toString();ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info",userInfo )).build();// 6.放行return chain.filter(swe);}/*** 判断路径是否需要拦截* @param antPath* @return*/private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}// 优先级@Overridepublic int getOrder() {return 0;}
}
因为我采用了JWT令牌进行校验,所以引入了JWT工具类,进行令牌的获取与解析。
至于AuthProperties是配置了不需要鉴权就能访问的路径。
经测试,网关已经可以完成登录校验并获取登录用户身份信息。
问题:当网关将请求转发到微服务时,微服务如何获取用户身份,我们不可能每个微服务都写一个拦截器去得到用户身份信息。
解决方案:将用户信息以请求头的方式传递到下游微服务。然后微服务可以从请求头中获取登录用户信息(上述代码第五步已经完成了)。考虑到微服务内部可能很多地方都需要用到登录用户信息,因此我们可以利用SpringMVC的拦截器来实现登录用户信息获取,并存入ThreadLocal。
拦截器流程图
提供一个用于保存登录用户的ThreadLocal工具UserContext:
public class UserContext {private static final ThreadLocal<Long> tl = new ThreadLocal<>();/*** 保存当前登录用户信息到ThreadLocal* @param userId 用户id*/public static void setUser(Long userId) {tl.set(userId);}/*** 获取当前登录用户信息* @return 用户id*/public static Long getUser() {return tl.get();}/*** 移除当前登录用户信息*/public static void removeUser(){tl.remove();}
}
在公用模块下定义一个拦截器:
public class UserInfoInterceptor implements HandlerInterceptor {/*** 请求拦截器* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的 用户信息String userInfo = request.getHeader("user-info");// 2.判断是否为空if (StringUtils.isNotBlank(userInfo)) {UserContext.setUser(Long.valueOf(userInfo));}// 3.放行return true;}/*** 响应拦截器* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserContext.removeUser();}
}
编写SpringMVC
的配置类,配置登录拦截器:
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}
注意:这个配置类默认是不会生效的,基于SpringBoot的自动装配原理,我们要将其添加到resources
目录下的META-INF/spring.factories
文件中:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig
经测试,服务得到网关传递的用户身份信息。
服务信息鉴权
问题:因为之前编写的过滤器和拦截器功能,微服务可以轻松获取登录用户信息。但是,有时候请求到达微服务后还需要调用其它多个微服务。我们没有实现服务之间的用户身份信息的传递。
解决方案:由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头。
因为我们之前微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。所以我们可以采用Feign中提供的一个拦截器接口:feign.RequestInterceptor。
在公用模块下定义一个userInfoRequestInterceptor
/*** feign请求拦截器 微服务之间的远程调用时,将当前登录用户的userId传递给目标服务* @return*/@Beanpublic RequestInterceptor userInfoRequestInterceptor() {return new RequestInterceptor() {public void apply(RequestTemplate requestTemplate) {Long userId = UserContext.getUser();if (userId != null) {requestTemplate.header("user-info", userId.toString());}}};}
总结:
- 为了实现网关处简便的登录校验,我们采用了GlobalFilter;
- 为了实现网关传递用户信息到多个微服务,我们采用了UserInfoInterceptor ;
- 为了实现微服务之间用户身份信息传递,我们采用了userInfoRequestInterceptor。
相关文章:
Gateway:网关路由与登录鉴权
在微服务架构中,用户登录和身份校验的处理方式确实与单体应用有所不同。在单体架构中,一旦用户通过身份验证,其会话信息可以在整个应用范围内共享,所有模块都能访问到用户信息。然而,在微服务架构下,每个服…...
【MySQL篇】MySQL内置函数
目录 1,日期函数 2,字符串函数 3,数学函数 4,其他函数 实战OJ 1,日期函数 日期类型在之前文章【数据类型】中有描述 传送门:【MySQL篇】数据类型_mysql 数据类型-CSDN博客 函数名称描述current_dat…...
爬虫案例十三js逆向模拟登录中大网校
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、网站分析二、代码 前言 提示:这里可以添加本文要记录的大概内容: js 逆向模拟登录中大网校 提示:以下是本篇文章正文内…...
高效编程指南:PyCharm与DeepSeek的完美结合
DeepSeek接入Pycharm 前几天DeepSeek的充值窗口又悄悄的开放了,这也就意味着我们又可以丝滑的使用DeepSeek的API进行各种辅助性工作了。本文我们来聊聊如何在代码编辑器中使用DeepSeek自动生成代码。 注:本文适用于所有的JetBrains开发工具,…...
前馈神经网络 - 参数学习(优化问题)
神经网络的参数学习比线性模型要更加困难,主要原因有两点:(1)非凸优化问题和 (2)梯度消失问题,本文我们来学习和分析这两类问题。 一、非凸优化问题 1、非凸优化问题演示: 神经网络的优化问题是一个非凸优化问题。 …...
AI 大模型统一集成|如何封装多个大模型 API 调用
🌟 在这系列文章中,我们将一起探索如何搭建一个支持大模型集成项目 NexLM 的开发过程,从 架构设计 到 代码实战,逐步搭建一个支持 多种大模型(GPT-4、DeepSeek 等) 的 一站式大模型集成与管理平台ÿ…...
C语言学习day25:WinAPI编程进阶06-游戏辅助窗体监视热键讲解
我们上一章已经绘制出了植物大战僵尸的一个页面 但是我们要怎么样去判断,用户是否按了F1呢。好的接下来就是我们的内容,监视热键。 思路: 我们按下的是键盘,因此我们得用键盘消息,也是:WM_KEYDOWN 同时要…...
JVM常用概念之常量
问题 final修饰的字段就一定是不能重新赋值吗? 基础知识 常量变量是使用常量表达式初始化的原始类型或 String 类型的最终变量。变量是否为常量变量可能对类初始化、二进制兼容性和明确赋值有影响。 —Java 语言规范 实验 用例源码-重新赋值 import java.lang.reflect.Fie…...
【Vue】el-dialog的2种封装方法(父子组件双向通信),$emit触发父事件/.sync修饰符双向绑定
🤵 作者:coderYYY 🧑 个人简介:前端程序媛,目前主攻web前端,后端辅助,其他技术知识也会偶尔分享🍀欢迎和我一起交流!🚀(评论和私信一般会回!!) 👉 个人专栏推荐:《前端项目教程以及代码》 前言 在现代Vue.js开发中,el-dialog组件作为ElementUI库中的一个…...
解决远程主机允许路由转发 【原理扫描】:将/proc/sys/net/ipv4/ip_forward 置为0
解决远程主机允许路由转发 【原理扫描】:将/proc/sys/net/ipv4/ip_forward 置为0 解决远程主机允许路由转发 【原理扫描】:将/proc/sys/net/ipv4/ip_forward 置为0问题描述解决方案临时修改永久生效验证配置 影响 解决远程主机允许路由转发 【原理扫描】…...
c++20 Concepts的简写形式与requires 从句形式
c20 Concepts的简写形式与requires 从句形式 原始写法(简写形式)等效写法(requires 从句形式)关键区别说明:组合多个约束的示例:两种形式的编译结果:更复杂的约束示例:标准库风格的约…...
安装oVirt环境
1. oVirt Engine 硬件要求 资源最低推荐 中央处理器 双核 x86_64 CPU. 一个四核 x86_64 CPU 或多个双核 x86_64 CPU。 记忆 4 GB 的可用系统 RAM(如果未安装 Data Warehouse 且现有进程未占用内存)。 16 GB 的系统 RAM。 硬盘 25 GB 本地可访问的…...
【 <一> 炼丹初探:JavaWeb 的起源与基础】之 Tomcat 的工作原理:从启动到请求处理的流程
<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、Tomcat…...
【认识OpenThread协议】
OpenThread 是一种基于 IPv6 、IEEE 802.15.4 标准的低功耗无线 Mesh 网络协议,主要用于智能家居、物联网设备等场景。它的设计目标是实现设备之间的高效通信、低功耗运行和高可靠性。 OpenThread官方文档 ① 特性 低功耗: 适合电池供电的设备。 Mesh 网络: 支持多…...
Qt入门笔记
目录 一、前言 二、创建Qt项目 2.1、使用向导创建 2.2、最简单的Qt应用程序 2.2.1、main函数 2.2.2、widget.h文件 2.2.3、widget.cpp文件 2.3、Qt按键Botton 2.3.1、创建一个Botton 2.3.2、信号与槽 2.3.3、按键使用信号与槽的方法 2.4、文件Read与Write-QFile类 2…...
【前端】【nuxt】几种在 Nuxt 客户端使用console的方式
方法1:在Vue生命周期钩子中使用 只在客户端执行的钩子(如mounted)中打印: export default {mounted() {console.log(仅在客户端显示, this.$route.path)} }方法2:通过环境判断 使用process.client判断当前环境&…...
安装 ubuntu 2404 LTS 服务器 设置 服务器名称
安装 ubuntu服务器 设置 服务器名称 hostname 打开终端(Terminal),通过快捷键CtrlAltT或在应用程序中搜索"终端"来打开;在终端中输入以下命令:hostname,然后按下回车键即可查看本机服务器名称。…...
C语言一维数组
学习任何数据结构,都可以分为三个主要步骤: 了解基本概念:首先,我们需要理解数据结构的基本概念。以数组为例,首先要知道什么是数组,数组的定义是什么。数组是一种存储固定大小的元素集合的数据结构,它的元素类型是统一的,且通过索引访问。 了解数组的构造和内存分布:…...
霍夫变换法是基于传统视觉特征的道路车道线检测算法中的一种经典方法
霍夫变换法是基于传统视觉特征的道路车道线检测算法中的一种经典方法,以下是对它的详细介绍: 基本原理 霍夫变换的基本思想是将图像空间中的点映射到参数空间中,通过在参数空间中寻找峰值来确定图像中特定形状的参数。在车道线检测中&#…...
静态时序分析:SDC约束命令set_ideal_latency详解
相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 当使用set_ideal_network命令将当前设计中的一组端口或引脚标记为理想网络源后,理想属性会沿着组合逻辑进行传播,理想网络中的线网和单元…...
DeepSeek引领端侧AI革命,边缘智能重构AI价值金字塔
目录 一、AI从“技术炫耀”到“价值兑现” 二、边缘侧部署:从技术挑战到商业必然 三、小规模模型:精度与效率的再平衡 四、Coovally的前瞻性:降低边缘AI开发门槛 五、生产级部署:跨越从实验室到车间的鸿沟 六、未来演进&…...
完整例子和调用关系qt OpenGL
项目结构 首先,你需要在 Qt 项目中创建一个类,继承自 QOpenGLWidget 来进行 OpenGL 渲染。文件结构如下: - main.cpp - MyOpenGLWidget.h - MyOpenGLWidget.cpp - vertex_shader.glsl - fragment_shader.glsl 1. main.cpp 这是 Qt 项目的入口…...
SpringBoot缓存抽象:@Cacheable与缓存管理器配置
文章目录 引言一、SpringBoot缓存抽象概述二、Cacheable注解详解2.1 Cacheable的关键属性 三、缓存管理器配置四、自定义键生成策略五、缓存同步与失效策略六、SpringBoot缓存最佳实践总结 引言 缓存是提升应用性能的关键技术,SpringBoot提供了强大的缓存抽象层&am…...
环路广播风暴演示图
以下是环路广播风暴的演示图及其说明: 环路广播风暴演示图 ----------------- ----------------- | Switch A | | Switch B | | | | | | [Port1]--------------------------[Port1] |…...
【webrtc debug tools】 rtc_event_log_to_text
一、rtc_event_log 简介 在学习分析webrtc的过程中,发现其内部提供了一个实时数据捕获接口RtcEventLog。通过该接口可以实时捕获进出webrtc的RTP报文头数据、音视频配置参数、webrtc的探测数据等。其内容实现可参考RtcEventLogImpl类的定义。其文件所在路径 loggin…...
【统计至简】【古典概率模型】联合概率、边缘概率、条件概率、全概率
联合概率、边缘概率、条件概率 联合概率边缘概率条件概率全概率 一副标准扑克牌有 54 张,包括 52 张常规牌(13 个点数,每个点数有 4 种花色)和 2 张王(大、小王)。我们从中随机抽取一张牌,定义以…...
Linux 字符设备驱动实例
编写驱动程序,并将内核模块加载到内核中,等待被用户程序调用。 在控制台中借助第一步申请到的设备号,使用 mknod 命令创建一个设备节点,并拟一个设备名称。 在用户程序中,使用 open 打开第二步中的设备名称ÿ…...
【git】【网络】【项目配置运行】HTTP 协议的微型简易 Web 服务器---tinyEasyMuduoWebServer
【git】【网络】【项目配置运行】HTTP 协议的微型简易 Web 服务器—tinyEasyMuduoWebServer csdn项目: 原文链接:https://blog.csdn.net/weixin_45178775/article/details/122257814 github链接:https://github.com/wyewyewye/tinyEasyMuduo…...
STM32驱动OLED屏幕全解析:从原理到温度显示实战(上) | 零基础入门STM32第五十三步
主题内容教学目的/扩展视频OLED显示屏重点课程电路原理,手册分析,驱动程序。初始化,清屏,ASCII字库,显示分区。调用显示函数。做带有加入图形和汉字显示的RTC时钟界面。讲字库的设计原理。 师从洋桃电子,杜…...
2024年9月中国电子学会青少年软件编程(Python)等级考试试卷(三级)答案 + 解析
更多真题在线练习系统:历年真题在线练习系统 一、单选题 1、以下表达式的值为True的是?( ) A. all( ,1,2,3) B. any([]) C. bool(abc) D. divmod(6,0) 正确答案:C 答案解析:A和B选项,Fal…...
苍穹外卖实战附源码-DAY1
一、打开项目的Nginx 1.通过nginx成功打开包装后的前端网页 二、导入sky的数据库 sky.sql 数据 CREATE DATABASE IF NOT EXISTS sky_take_out ; USE sky_take_out;DROP TABLE IF EXISTS address_book; CREATE TABLE address_book (id bigint NOT NULL AUTO_INCREMENT COMMEN…...
Spring 框架学习
技术体系结构 总体技术体系 单一架构 一个项目,一个工程,导出为一个 war 包,在一个 Tomcat 上运行,也叫 all in one。 单一架构,项目主要应用技术框架为:Spring、SpringMVC 、Mybatis。 分布式架构 一个…...
股票交易所官方api接口有哪些?获取和使用需要满足什么条件
炒股自动化:申请官方API接口,散户也可以 python炒股自动化(0),申请券商API接口 python炒股自动化(1),量化交易接口区别 Python炒股自动化(2):获取…...
NAT NAPT
NAT NAT(Network Address Translation,网络地址转换) 主要用于在不同网络(如私有网络和公共互联网)之间进行 IP 地址转换,解决IP 地址短缺问题,并提供一定的安全性。 IPv4 地址是 32 位…...
调优案例一:堆空间扩容提升吞吐量实战记录
📝 调优案例一:堆空间扩容提升吞吐量实战记录 🔧 调优策略:堆空间扩容三部曲 # 原配置(30MB堆空间) export CATALINA_OPTS"$CATALINA_OPTS -Xms30m -Xmx30m"# 新配置(扩容至120MB&am…...
对比 Vue2 选项式 API 与 Vue3 setup 语法
对比 Vue2 选项式 API 与 Vue3 setup 语法 1. 代码组织方式 Vue2 选项式 API 通过独立的选项(data, methods, computed, watch, 生命周期钩子等)组织代码。 export default {data() {return { count: 0 };},methods: {increment() { this.count; }},mou…...
ragflow-组件可视化工具 es默认用户名elastic
以下是针对各个后台组件的可视化工具及配置指南,基于您提供的环境变量和端口设置: 1. Elasticsearch 可视化工具: Kibana(官方推荐)、Cerebro、ElasticHQ访问方式: Kibana(集成管理): URL: http://<主机…...
MATLAB代码开发实战:从入门到高效应用
一、MATLAB生态系统的核心优势 (扩展原有内容,增加行业数据) MATLAB在全球工程领域的市场占有率已达67%(2024年IEEE统计),其核心优势体现在: 矩阵运算速度比传统编程快3-5倍包含22个专业工具箱…...
GStreamer —— 2.18、Windows下Qt加载GStreamer库后运行 - “播放教程 6:音频可视化“(附:完整源码)
运行效果 介绍 GStreamer 带有一组将音频转换为视频的元素。他们 可用于科学可视化或为您的音乐增添趣味 player 的本教程展示了: • 如何启用音频可视化 • 如何选择可视化元素 启用音频可视化实际上非常简单。设置相应的标志,当纯音频流为 found&#…...
50个经典的python库
本文整理了50个可以迅速掌握的经典Python库,了解它们的用途,无论你是刚踏上编程之路,还是希望在Python的世界里更加深入,这50个库都能帮助你快速起飞。 1. Taipy Taipy是一个开源Python库,用于轻松的端到端应用程序开…...
PostgreSQL学习笔记:PostgreSQL vs MySQL
PostgreSQL 和 MySQL 都是广泛使用的关系型数据库管理系统,它们有以下一些对比: 一、功能特性 1. 数据类型支持 PostgreSQL:支持丰富的数据类型,包括数组、JSON、JSONB、范围类型、几何类型等。对于复杂数据结构的存储和处理非…...
【CentOS】搭建Radius服务器
目录 背景简介:Radius是什么?Radius服务器验证原理搭建Radius服务器环境信息yum在线安装配置FreeRADIUS相关文件clients.conf文件users文件重启服务 验证 参考链接 背景 在项目中需要用到Radius服务器作为数据库代理用户的外部验证服务器,做…...
C#枚举(Enum)详解
在 C# 中,枚举(Enum) 是一种值类型,用于定义一组命名的常量值,提高代码的可读性和可维护性。以下是枚举的核心概念、用法和最佳实践: 1. 枚举的核心特性 类型安全:避免使用魔法数字&…...
DeepSeek进阶应用(一):结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)
🌟前言: 在软件开发、项目管理和系统设计等领域,图表是表达复杂信息的有效工具。随着AI助手如DeepSeek的普及,我们现在可以更轻松地创建各种专业图表。 名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》 创作者&…...
Golang | Gin(简洁版)
文章目录 安装使用RESTful API响应页面获取请求参数路由讲解中间件 安装使用 Gin 是一个 golang 的微框架,封装比较优雅,API 友好,源代码比较明确。具有快速灵活,容错方便等特点。其实对于 golang 而言,web 框架的依赖…...
【C++ 系列文章 基础 01 -- std::string 与 fmt::format】
文章目录 Overview1. C 中的 std::string 简介2. fmt::format 格式化函数简介3. 示例代码解析4. 应用场景与优势2. std::string 与 fmt::format 简介std::stringfmt::format 3. 代码解析3.1 格式化字符串生成3.2 调用函数 cmd_handler3.3 返回 id_code 4. 代码整体流程与应用场…...
有效封装一个 WebSocket 供全局使用
前言 在现代 Web 应用中,实时通信已经成为越来越重要的一部分。而 WebSocket 技术的出现,使得实时通信变得更加高效和便捷。 WebSocket 协议是一种基于 TCP 协议的双向通信协议,它能够在客户端和服务器之间建立起持久性的连接,从…...
使用expect工具实现远程批量修改服务器密码
使用expect工具实现远程批量修改服务器密码 linux服务器安装Expect工具 1、首先查看系统中是否有安装expect。 # whereis expect 2、Expect工具是依赖tcl的,需要先安装tcl #wget https://sourceforge.net/projects/tcl/files/Tcl/8.4.19/tcl8.4.19-src.tar.gz …...
算法日记39:洛谷P4170涂色(区间DP)
一、题目 二、题解: 1、题目解析: 1)刚刚开始阅读到题目,我们发现并没有什么思路,因此我们可以尝试来模拟一下样例的情况 2)通过观察我们发现 n 2 : n2: n2:可以拆分成 1 1 11 11来解决问题 n 3 : n3:…...
Python学习第十三天
正则表达式 什么是正则表达式:简单来说就是通过特殊符号匹配想要的字符串,正则表达式本身就是基于字符串的一套搜索规则,掌握了正则表达式对于字符串有了更深的把握和理解。 概念 官网概念:正则表达式(Regular Expres…...