基于SpringBoot IP黑白名单的实现
业务场景
IP黑白名单是网络安全管理中常见的策略工具,用于控制网络访问权限,根据业务场景的不同,其应用范围广泛,以下是一些典型业务场景:
-
服务器安全防护:
- 黑名单:可以用来阻止已知的恶意IP地址或曾经尝试攻击系统的IP地址,防止这些来源对服务器进行未经授权的访问、扫描、攻击等行为。
- 白名单:仅允许特定IP或IP段访问关键服务,比如数据库服务器、内部管理系统等,实现最小授权原则,降低被未知风险源入侵的可能性。
-
网站安全防护:
- 黑名单:对于频繁发起恶意请求、爬取数据、DDoS攻击等活动的IP,将其加入黑名单以限制其对网站的访问。
- 白名单:如果只希望特定合作伙伴、内部员工或特定区域用户访问网站内容,则可通过白名单来限定合法访问者的范围。
-
API接口保护:
- 对于对外提供的API接口,通过设置IP黑白名单,确保只有经过认证或信任的系统和客户端才能调用接口。
比如比较容易被盗刷的短信接口、文件接口,都需要添加IP黑白名单加以限制。
核心实现
获取客户端IP地址
@UtilityClass
public class IpUtils {private final String UNKNOWN = "unknown";private final String X_FORWARDED_FOR = "X-Forwarded-For";private final String PROXY_CLIENT_IP = "Proxy-Client-IP";private final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";private final Pattern COMMA_SEPARATED_VALUES_PATTERN = Pattern.compile("\s*,\s*");/*** 默认情况下内网代理的子网可以是(后面有需要可以进行配置):* 1. 10/8* 2. 192.168/16* 3. 169.254/16* 4. 127/8* 5. 172.16/12* 6. ::1*/private final Pattern INTERNAL_PROXIES = Pattern.compile("10\.\d{1,3}\.\d{1,3}\.\d{1,3}|" +"192\.168\.\d{1,3}\.\d{1,3}|" +"169\.254\.\d{1,3}\.\d{1,3}|" +"127\.\d{1,3}\.\d{1,3}\.\d{1,3}|" +"172\.1[6-9]\.\d{1,3}\.\d{1,3}|" +"172\.2[0-9]\.\d{1,3}\.\d{1,3}|" +"172\.3[0-1]\.\d{1,3}\.\d{1,3}|" +"0:0:0:0:0:0:0:1|::1");/*** 获取请求的IP** @return 请求的IP*/public String getIp() {var requestAttributes = RequestContextHolder.getRequestAttributes();if (Objects.isNull(requestAttributes)) {return null;}var request = ((ServletRequestAttributes) requestAttributes).getRequest();var ip = getRemoteIp(request);if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader(PROXY_CLIENT_IP);}if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader(WL_PROXY_CLIENT_IP);}if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}/*** 获取客户端真实IP地址,防止使用X-Forwarded-For进行IP伪造攻击,防御思路见类注释** @return 真实IP地址*/private String getRemoteIp(HttpServletRequest request) {var remoteIp = request.getRemoteAddr();var isInternal = INTERNAL_PROXIES.matcher(remoteIp).matches();if (isInternal) {var concatRemoteIpHeaderValue = new StringBuilder();for (var e = request.getHeaders(X_FORWARDED_FOR); e.hasMoreElements(); ) {if (concatRemoteIpHeaderValue.length() > 0) {concatRemoteIpHeaderValue.append(", ");}concatRemoteIpHeaderValue.append(e.nextElement());}var remoteIpHeaderValue = commaDelimitedListToArray(concatRemoteIpHeaderValue.toString());for (var i = remoteIpHeaderValue.length - 1; i >= 0; i--) {var currentRemoteIp = remoteIpHeaderValue[i];if (!INTERNAL_PROXIES.matcher(currentRemoteIp).matches()) {return currentRemoteIp;}}return null;} else {return remoteIp;}}private String[] commaDelimitedListToArray(String commaDelimitedStrings) {return (commaDelimitedStrings == null || commaDelimitedStrings.isEmpty())? new String[0]: COMMA_SEPARATED_VALUES_PATTERN.split(commaDelimitedStrings);}
}
获取到客户端IP后,我们只要比对客户端IP是否在配置的白名单/黑名单中即可。
为了简化使用,可以采用注解的方式进行拦截。
新增注解@IpCheck
/*** IP白名单校验*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Inherited
public @interface IpCheck {/*** 白名单IP列表,支持${...}*/@AliasFor("whiteList")String value() default "";/*** 白名单IP列表,支持${...}*/@AliasFor("value")String whiteList() default "";/*** 黑名单IP列表,支持${...}*/String blackList() default "";}
新增IpCheckHandlerInterceptorImpl
我们实现HandlerInterceptor,在接口上进行拦截,如果不满足配置的黑白名单,则抛出异常。
/*** @author <a href="mailto:gcwm99@gmail.com">gcdd1993</a>* Created by gcdd1993 on 2023/9/20*/
@Component
public class IpCheckHandlerInterceptorImpl implements HandlerInterceptor, EmbeddedValueResolverAware {private StringValueResolver stringValueResolver;@Overridepublic boolean preHandle(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler) {// 检查是否有IpWhitelistCheck注解,并且是否开启IP白名单检查if (!(handler instanceof HandlerMethod)) {return true; // 如果没有注解或者注解中关闭了IP白名单检查,则继续处理请求}var handlerMethod = (HandlerMethod) handler;var method = handlerMethod.getMethod();var annotation = AnnotationUtils.getAnnotation(method, IpCheck.class);if (annotation == null) {return true;}var clientIp = IpUtils.getIp();// 检查客户端IP是否在白名单中var whiteList = Stream.of(Optional.ofNullable(stringValueResolver.resolveStringValue(annotation.whiteList())).map(it -> it.split(",")).orElse(new String[]{})).filter(StringUtils::hasText).map(String::trim).collect(Collectors.toUnmodifiableSet());if (!whiteList.isEmpty() && whiteList.contains(clientIp)) {return true; // IP在白名单中,继续处理请求}var blackList = Stream.of(Optional.ofNullable(stringValueResolver.resolveStringValue(annotation.blackList())).map(it -> it.split(",")).orElse(new String[]{})).filter(StringUtils::hasText).map(String::trim).collect(Collectors.toUnmodifiableSet());if (!blackList.isEmpty() && !blackList.contains(clientIp)) {return true; // IP不在黑名单中,继续处理请求}// IP不在白名单中,可以返回错误响应或者抛出异常// 例如,返回一个 HTTP 403 错误throw new RuntimeException("Access denied, remote ip " + clientIp + " is not allowed.");}@Overridepublic void setEmbeddedValueResolver(StringValueResolver resolver) {this.stringValueResolver = resolver;}
}
自动装配
核心逻辑写完了,该怎么使用呢?为了达到开箱即用的效果,我们可以接着新增自动装配的代码
新建IpCheckConfig
实现WebMvcConfigurer接口,添加接口拦截器
/*** @author <a href="mailto:gcwm99@gmail.com">gcdd1993</a>* Created by gcdd1993 on 2024/1/24*/
public class IpCheckConfig implements WebMvcConfigurer {@Resourceprivate IpCheckHandlerInterceptorImpl ipCheckHandlerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(ipCheckHandlerInterceptor);}}
新建@EnableIpCheck
参考@EnableScheduling的实现,自己实现一个@EnableIpCheck,该注解可以控制功能是否启用
/*** @author <a href="mailto:gcwm99@gmail.com">gcdd1993</a>* Created by gcdd1993 on 2024/1/24*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@Documented
@ComponentScan("xxx.ip") // 这里是IpCheckConfig的包名
@Import(IpCheckConfig.class)
public @interface EnableIpCheck {
}
业务测试
简单地用代码来试验下效果
新建SampleApplication
@SpringBootApplication
@EnableIpCheck
public class SampleApplication {public static void main(String[] args) {SpringApplication.run(SampleApplication.class, args);}}
新建测试接口
@RestController
@RequestMapping("/sample/ip-checker")
public class IpCheckSample {@GetMapping("/white")@IpCheck(value = "0:0:0:0:0:0:0:1")String whiteList() {return "127.0.0.1";}@GetMapping("/black")@IpCheck(blackList = "0:0:0:0:0:0:0:1")String blackList() {return "127.0.0.1";}/*** 同时配置白名单和黑名单,要求IP既在白名单,并且不在黑名单,否则抛出异常*/@GetMapping("/all")@IpCheck(value = "0:0:0:0:0:0:0:1", blackList = "0:0:0:0:0:0:0:1")String all() {return "127.0.0.1";}/*** 同时配置白名单和黑名单,要求IP既在白名单,并且不在黑名单,否则抛出异常* 支持解析Spring 配置文件*/@GetMapping("/config")@IpCheck(value = "${digit.ip.check.white-list}", blackList = "${digit.ip.check.black-list}")String config() {return "127.0.0.1";}/*** 同时配置白名单和黑名单,要求IP既在白名单,并且不在黑名单,否则抛出异常* 支持解析Spring 配置文件*/@GetMapping("/black-config")@IpCheck(blackList = "${digit.ip.check.black-list}")String blackConfig() {return "127.0.0.1";}}
由于本机请求IP地址是0:0:0:0:0:0:0:1,所以这里使用0:0:0:0:0:0:0:1而不是127.0.0.1。
访问/sample/ip-checker/white
接口返回127.0.0.1
访问/sample/ip-checker/black
java.lang.RuntimeException: Access denied, remote ip 0:0:0:0:0:0:0:1 is not allowed.
访问/sample/ip-checker/all
接口返回127.0.0.1
- 既配置白名单,也配置黑名单,需要既不在白名单,同时在黑名单里,才会拦截。
修改配置
digit:ip:check:white-list: 127.0.0.1, 192.168.1.1, 192.168.1.2black-list: 127.0.0.1, 192.168.1.1, 192.168.1.2,0:0:0:0:0:0:0:1
访问/sample/ip-checker/black-config
java.lang.RuntimeException: Access denied, remote ip 0:0:0:0:0:0:0:1 is not allowed.
最后,可以结合配置中心,以便配置后立即生效。
业务场景
IP黑白名单是网络安全管理中常见的策略工具,用于控制网络访问权限,根据业务场景的不同,其应用范围广泛,以下是一些典型业务场景:
-
服务器安全防护:
- 黑名单:可以用来阻止已知的恶意IP地址或曾经尝试攻击系统的IP地址,防止这些来源对服务器进行未经授权的访问、扫描、攻击等行为。
- 白名单:仅允许特定IP或IP段访问关键服务,比如数据库服务器、内部管理系统等,实现最小授权原则,降低被未知风险源入侵的可能性。
-
网站安全防护:
- 黑名单:对于频繁发起恶意请求、爬取数据、DDoS攻击等活动的IP,将其加入黑名单以限制其对网站的访问。
- 白名单:如果只希望特定合作伙伴、内部员工或特定区域用户访问网站内容,则可通过白名单来限定合法访问者的范围。
-
API接口保护:
- 对于对外提供的API接口,通过设置IP黑白名单,确保只有经过认证或信任的系统和客户端才能调用接口。
比如比较容易被盗刷的短信接口、文件接口,都需要添加IP黑白名单加以限制。
核心实现
获取客户端IP地址
@UtilityClass
public class IpUtils {private final String UNKNOWN = "unknown";private final String X_FORWARDED_FOR = "X-Forwarded-For";private final String PROXY_CLIENT_IP = "Proxy-Client-IP";private final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";private final Pattern COMMA_SEPARATED_VALUES_PATTERN = Pattern.compile("\s*,\s*");/*** 默认情况下内网代理的子网可以是(后面有需要可以进行配置):* 1. 10/8* 2. 192.168/16* 3. 169.254/16* 4. 127/8* 5. 172.16/12* 6. ::1*/private final Pattern INTERNAL_PROXIES = Pattern.compile("10\.\d{1,3}\.\d{1,3}\.\d{1,3}|" +"192\.168\.\d{1,3}\.\d{1,3}|" +"169\.254\.\d{1,3}\.\d{1,3}|" +"127\.\d{1,3}\.\d{1,3}\.\d{1,3}|" +"172\.1[6-9]\.\d{1,3}\.\d{1,3}|" +"172\.2[0-9]\.\d{1,3}\.\d{1,3}|" +"172\.3[0-1]\.\d{1,3}\.\d{1,3}|" +"0:0:0:0:0:0:0:1|::1");/*** 获取请求的IP** @return 请求的IP*/public String getIp() {var requestAttributes = RequestContextHolder.getRequestAttributes();if (Objects.isNull(requestAttributes)) {return null;}var request = ((ServletRequestAttributes) requestAttributes).getRequest();var ip = getRemoteIp(request);if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader(PROXY_CLIENT_IP);}if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader(WL_PROXY_CLIENT_IP);}if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}/*** 获取客户端真实IP地址,防止使用X-Forwarded-For进行IP伪造攻击,防御思路见类注释** @return 真实IP地址*/private String getRemoteIp(HttpServletRequest request) {var remoteIp = request.getRemoteAddr();var isInternal = INTERNAL_PROXIES.matcher(remoteIp).matches();if (isInternal) {var concatRemoteIpHeaderValue = new StringBuilder();for (var e = request.getHeaders(X_FORWARDED_FOR); e.hasMoreElements(); ) {if (concatRemoteIpHeaderValue.length() > 0) {concatRemoteIpHeaderValue.append(", ");}concatRemoteIpHeaderValue.append(e.nextElement());}var remoteIpHeaderValue = commaDelimitedListToArray(concatRemoteIpHeaderValue.toString());for (var i = remoteIpHeaderValue.length - 1; i >= 0; i--) {var currentRemoteIp = remoteIpHeaderValue[i];if (!INTERNAL_PROXIES.matcher(currentRemoteIp).matches()) {return currentRemoteIp;}}return null;} else {return remoteIp;}}private String[] commaDelimitedListToArray(String commaDelimitedStrings) {return (commaDelimitedStrings == null || commaDelimitedStrings.isEmpty())? new String[0]: COMMA_SEPARATED_VALUES_PATTERN.split(commaDelimitedStrings);}
}
获取到客户端IP后,我们只要比对客户端IP是否在配置的白名单/黑名单中即可。
为了简化使用,可以采用注解的方式进行拦截。
新增注解@IpCheck
/*** IP白名单校验*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Inherited
public @interface IpCheck {/*** 白名单IP列表,支持${...}*/@AliasFor("whiteList")String value() default "";/*** 白名单IP列表,支持${...}*/@AliasFor("value")String whiteList() default "";/*** 黑名单IP列表,支持${...}*/String blackList() default "";}
新增IpCheckHandlerInterceptorImpl
我们实现HandlerInterceptor,在接口上进行拦截,如果不满足配置的黑白名单,则抛出异常。
/*** @author <a href="mailto:gcwm99@gmail.com">gcdd1993</a>* Created by gcdd1993 on 2023/9/20*/
@Component
public class IpCheckHandlerInterceptorImpl implements HandlerInterceptor, EmbeddedValueResolverAware {private StringValueResolver stringValueResolver;@Overridepublic boolean preHandle(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler) {// 检查是否有IpWhitelistCheck注解,并且是否开启IP白名单检查if (!(handler instanceof HandlerMethod)) {return true; // 如果没有注解或者注解中关闭了IP白名单检查,则继续处理请求}var handlerMethod = (HandlerMethod) handler;var method = handlerMethod.getMethod();var annotation = AnnotationUtils.getAnnotation(method, IpCheck.class);if (annotation == null) {return true;}var clientIp = IpUtils.getIp();// 检查客户端IP是否在白名单中var whiteList = Stream.of(Optional.ofNullable(stringValueResolver.resolveStringValue(annotation.whiteList())).map(it -> it.split(",")).orElse(new String[]{})).filter(StringUtils::hasText).map(String::trim).collect(Collectors.toUnmodifiableSet());if (!whiteList.isEmpty() && whiteList.contains(clientIp)) {return true; // IP在白名单中,继续处理请求}var blackList = Stream.of(Optional.ofNullable(stringValueResolver.resolveStringValue(annotation.blackList())).map(it -> it.split(",")).orElse(new String[]{})).filter(StringUtils::hasText).map(String::trim).collect(Collectors.toUnmodifiableSet());if (!blackList.isEmpty() && !blackList.contains(clientIp)) {return true; // IP不在黑名单中,继续处理请求}// IP不在白名单中,可以返回错误响应或者抛出异常// 例如,返回一个 HTTP 403 错误throw new RuntimeException("Access denied, remote ip " + clientIp + " is not allowed.");}@Overridepublic void setEmbeddedValueResolver(StringValueResolver resolver) {this.stringValueResolver = resolver;}
}
自动装配
核心逻辑写完了,该怎么使用呢?为了达到开箱即用的效果,我们可以接着新增自动装配的代码
新建IpCheckConfig
实现WebMvcConfigurer接口,添加接口拦截器
/*** @author <a href="mailto:gcwm99@gmail.com">gcdd1993</a>* Created by gcdd1993 on 2024/1/24*/
public class IpCheckConfig implements WebMvcConfigurer {@Resourceprivate IpCheckHandlerInterceptorImpl ipCheckHandlerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(ipCheckHandlerInterceptor);}}
新建@EnableIpCheck
参考@EnableScheduling的实现,自己实现一个@EnableIpCheck,该注解可以控制功能是否启用
/*** @author <a href="mailto:gcwm99@gmail.com">gcdd1993</a>* Created by gcdd1993 on 2024/1/24*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@Documented
@ComponentScan("xxx.ip") // 这里是IpCheckConfig的包名
@Import(IpCheckConfig.class)
public @interface EnableIpCheck {
}
业务测试
简单地用代码来试验下效果
新建SampleApplication
@SpringBootApplication
@EnableIpCheck
public class SampleApplication {public static void main(String[] args) {SpringApplication.run(SampleApplication.class, args);}}
新建测试接口
@RestController
@RequestMapping("/sample/ip-checker")
public class IpCheckSample {@GetMapping("/white")@IpCheck(value = "0:0:0:0:0:0:0:1")String whiteList() {return "127.0.0.1";}@GetMapping("/black")@IpCheck(blackList = "0:0:0:0:0:0:0:1")String blackList() {return "127.0.0.1";}/*** 同时配置白名单和黑名单,要求IP既在白名单,并且不在黑名单,否则抛出异常*/@GetMapping("/all")@IpCheck(value = "0:0:0:0:0:0:0:1", blackList = "0:0:0:0:0:0:0:1")String all() {return "127.0.0.1";}/*** 同时配置白名单和黑名单,要求IP既在白名单,并且不在黑名单,否则抛出异常* 支持解析Spring 配置文件*/@GetMapping("/config")@IpCheck(value = "${digit.ip.check.white-list}", blackList = "${digit.ip.check.black-list}")String config() {return "127.0.0.1";}/*** 同时配置白名单和黑名单,要求IP既在白名单,并且不在黑名单,否则抛出异常* 支持解析Spring 配置文件*/@GetMapping("/black-config")@IpCheck(blackList = "${digit.ip.check.black-list}")String blackConfig() {return "127.0.0.1";}}
由于本机请求IP地址是0:0:0:0:0:0:0:1,所以这里使用0:0:0:0:0:0:0:1而不是127.0.0.1。
访问/sample/ip-checker/white
接口返回127.0.0.1
访问/sample/ip-checker/black
java.lang.RuntimeException: Access denied, remote ip 0:0:0:0:0:0:0:1 is not allowed.
访问/sample/ip-checker/all
接口返回127.0.0.1
- 既配置白名单,也配置黑名单,需要既不在白名单,同时在黑名单里,才会拦截。
修改配置
digit:ip:check:white-list: 127.0.0.1, 192.168.1.1, 192.168.1.2black-list: 127.0.0.1, 192.168.1.1, 192.168.1.2,0:0:0:0:0:0:0:1
访问/sample/ip-checker/black-config
java.lang.RuntimeException: Access denied, remote ip 0:0:0:0:0:0:0:1 is not allowed.
最后,可以结合配置中心,以便配置后立即生效。
相关文章:
基于SpringBoot IP黑白名单的实现
业务场景 IP黑白名单是网络安全管理中常见的策略工具,用于控制网络访问权限,根据业务场景的不同,其应用范围广泛,以下是一些典型业务场景: 服务器安全防护: 黑名单:可以用来阻止已知的恶意IP地…...
Redis客户端之Redisson(二)Redisson分布式锁
一、原理: Redisson并没有通过setNx命令来实现加锁,而是基于 Redis 看⻔狗机制,自己实现了一套分布式锁逻辑。 1、加锁机制: 二、使用方法:...
掌握大语言模型技术: 推理优化
掌握大语言模型技术_推理优化 堆叠 Transformer 层来创建大型模型可以带来更好的准确性、少样本学习能力,甚至在各种语言任务上具有接近人类的涌现能力。 这些基础模型的训练成本很高,并且在推理过程中可能会占用大量内存和计算资源(经常性成…...
git如何导出提交记录及修改的文件清单?
导出git提交日志及修改文件 # 所有人的提交记录 git log --pretty=format:"%ai,%an:%s" --since="10 day ago" >> ~/Desktop/commit10.log#某一个人的提交记录 git log --pretty=format:"%ai,%an:%s" --since="30 day ago" |...
从零开始:Ubuntu Server中MySQL 8.0的安装与Django数据库配置详解
Ubuntu系统纯净安装MySQL8.0 1、安装Mysql8.0 sudo apt install mysql-server2、检查MySQL状态 sudo systemctl status mysql如下所示看见Active: active (running)说明mysql状态正常 ● mysql.service - MySQL Community ServerLoaded: loaded (/lib/systemd/system/mysql…...
Vue基础知识
Vue Vue基础知识 v-bind:动态绑定属性值 Vue 修改,标签内也修改 在methods 中可以定义很多函数 在 data 中可以定义很多变量 v-if / v-show:对符合条件的元素进行展示 v-for:把数据遍历出现在网页中 案例 <!DOCTYPE html><html lang"e…...
瀑布流布局 (初版)
瀑布流布局 文章目录 瀑布流布局前言1. 背景2. 点⬇️🔗去体验效果如下图所示: 一、初版waterfall布局和问题暴露?1.效果图如下:2.暴露问题如下图所示:第一张问题图:第二张问题图: 3.HTML代码如…...
硕士毕业论文写作笔记
一、写作顺序 1.标题、研究问题、研究方法 2.文献综述(占比1/5-1/6) 3.论证章节 4.结论、不足、启示 5.处理图表、参考文献的格式 6.绪论或引言 7.摘要、关键词 8.查重、装订 http://【硕士毕业论文写不下去,多亏听了张博士的论文写…...
成本更低、更可控,云原生可观测新计费模式正式上线
云布道师 在上云开始使用云产品过程中,企业一定遇见过两件“讨厌”事: 难以理解的复杂计费逻辑,时常冒出“这也能收费”的感叹; 某个配置参数调节之后,云产品使用成本不可预估的暴涨。 可观测作为企业 IT 运维必须品…...
5.列表选择弹窗(BottomListPopup)
愿你出走半生,归来仍是少年! 环境:.NET 7、MAUI 从底部弹出的列表选择弹窗。 1.布局 <?xml version"1.0" encoding"utf-8" ?> <toolkit:Popup xmlns"http://schemas.microsoft.com/dotnet/2021/maui"xmlns…...
(十三)Head first design patterns原型模式(c++)
原型模式 原型模式就是就是对对象的克隆。有一些私有变量外界难以访问,而原型模式可以做到对原型一比一的复刻。 其关键代码为下面的clone方法。此方法将本对象进行复制传递出去。 class ConcretePrototype1 : public Prototype{ public:ConcretePrototype1(stri…...
Python基础之数据库操作
一、安装第三方库PyMySQL 1、在PyCharm中通过 【File】-【setting】-【Python Interpreter】搜索 PyMySQL进行安装 2、通过PyCharm中的 Terminal 命令行 输入: pip install PyMySQL 注:通过pip安装,可能会提示需要更新pip,这时可执行&#…...
redis-发布缓存
一.redis的发布订阅 什么 是发布和订阅 Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。 Redis 客户端可以订阅任意数量的频道。 Redis的发布和订阅 客户端订阅频道发布的消息 频道发布消息 订阅者就可…...
Stata17安装教程
文章目录 **Stata17安装教程**前言系统要求Windows:macOS:Linux: 软件下载正式安装1.下载Stata 17安装包2.双击Stata17.exe开启安装3.接受同意条款,然后继续安装4.选择想要安装的版本,Stata BE为基础版、Stata SE为特别…...
Java PDFBox 提取页数、PDF转图片
PDF 提取 使用Apache 的pdfbox组件对PDF文件解析读取和转图片。 Maven 依赖 导入下面的maven依赖: <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.30</version> &l…...
【代码随想录14】104.二叉树的最大深度 111.二叉树的最小深度 222.完全二叉树的节点个数
目录 104.二叉树的最大深度题目描述参考代码 111.二叉树的最小深度题目描述参考代码 222.完全二叉树的节点个数题目描述参考代码 104.二叉树的最大深度 题目描述 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径…...
AWS 专题学习 P10 (Databases、 Data Analytics)
文章目录 专题总览1. Databases1.1 选择合适的数据库1.2 数据库类型1.3 AWS 数据库服务概述Amazon RDSAmazon AuroraAmazon ElastiCacheAmazon DynamoDBAmazon S3DocumentDBAmazon NeptuneAmazon Keyspaces (for Apache Cassandra)Amazon QLDBAmazon Timestream 2. Data & …...
一键拥有你的GPT4
这几天我一直在帮朋友升级ChatGPT,现在已经可以闭眼操作了哈哈😝。我原本以为大家都已经用上GPT4,享受着它带来的巨大帮助时,但结果还挺让我吃惊的,还是有很多人仍苦于如何进行升级。所以就想着写篇教程来教会大家如何…...
幻兽帕鲁服务器数据备份
搭建幻兽帕鲁个人服务器,最近不少用户碰到内存不足、游戏坏档之类的问题。做好定时备份,才能轻松快速恢复游戏进度 这里讲一下如何定时将服务器数据备份到腾讯云轻量对象存储服务,以及如何在有需要的时候进行数据恢复。服务器中间的数据迁移…...
【Digester解析XML文件的三种方式】
Digester解析XML文件的三种方式 1. Digester解析XML文件的三种方式1.1 作用及依赖jar包 2. 重点和难点3. XML文件4. 通过不同的方式解析这个xml文件4.1 通过java编码方式解析(javabean存储)4.2 通过java编码方式解析(list和map存储࿰…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
