MybatisPlus防SQL注入实战:安全使用QueryWrapper与LambdaQueryWrapper
1. 从一次线上事故说起为什么MybatisPlus用户也需要警惕SQL注入去年我参与处理了一个线上服务的数据异常问题。一个基于Spring Boot和MybatisPlus开发的后台管理系统在某个查询接口被恶意调用后出现了用户数据泄露。开发团队的第一反应是“我们用的是MybatisPlusORM框架不是已经防注入了吗” 然而经过排查问题恰恰出在一个他们自认为“安全”的QueryWrapper动态条件拼接上。他们使用了wrapper.apply(“date_format(create_time, ‘%Y%m’) {0}”, userInput)这样的写法本意是进行日期格式化的匹配但攻击者通过精心构造的userInput最终绕过了预编译导致了SQL注入。这个案例非常典型它打破了许多开发者的一个固有认知使用了MybatisPlus或任何ORM框架就等于高枕无忧自动免疫SQL注入。事实上ORM框架提供的是一种“安全编程模型”和“安全工具”但工具能否被正确使用完全取决于开发者。MybatisPlus在默认、规范的使用下能极大程度地避免SQL注入但它也提供了许多灵活、强大的动态SQL构建方式这些方式如果使用不当就会成为安全漏洞的源头。SQL注入作为OWASP Top 10长期榜上有名的安全威胁其危害不言而喻数据泄露、数据篡改、甚至服务器被接管。对于MybatisPlus用户来说理解其防注入原理的边界明确哪些用法是“安全区”哪些是“危险区”是写出健壮代码的必备知识。这不是一个可选项而是每个使用该框架的开发者的责任。本文将彻底拆解MybatisPlus与SQL注入的攻防让你不仅知道“怎么用是安全的”更深入理解“为什么这样是安全的”以及“为什么那样做就危险了”。2. MybatisPlus防注入的核心基石SQL预编译与参数化查询要理解MybatisPlus如何防注入首先必须回到最根本的数据库访问安全机制参数化查询Prepared Statement。这是所有现代数据库访问层防御SQL注入的第一道也是最核心的一道防线。2.1 预编译机制是如何工作的当你直接拼接SQL字符串时代码可能是这样的String sql SELECT * FROM user WHERE name userName ;如果userName是admin OR 11最终的SQL就变成了SELECT * FROM user WHERE name admin OR 11这将导致查询条件永远为真返回所有用户数据。而参数化查询的做法截然不同String sql SELECT * FROM user WHERE name ?; PreparedStatement stmt connection.prepareStatement(sql); stmt.setString(1, userName);在这个例子中SQL语句SELECT * FROM user WHERE name ?会先被数据库驱动发送到数据库进行编译解析语法、确定执行计划。这个编译过程发生在传入具体参数值之前。那个问号?是一个占位符它代表一个“参数位置”而不是值的一部分。当你调用stmt.setString(1, userName)时无论userName的值是什么即使是admin OR 11数据库驱动都会将其作为一个完整的字符串值填充到已经编译好的SQL模板的对应占位符上。数据库引擎不会将这个值再作为SQL语法的一部分进行解析。关键区别在于在拼接SQL中用户输入被当成了SQL语句的“语法组成部分”在参数化查询中用户输入始终被当作纯粹的“数据值”。从数据库引擎的视角看它执行的两条语句本质上是不同的拼接语句执行(SELECT * FROM user WHERE name ‘admin‘ OR ‘1‘‘1‘)。参数化语句执行(预编译好的查询计划 参数‘admin\‘ OR \‘1\‘\‘1‘)。这里的单引号是字符串内容的一部分而不是SQL语法中的字符串界定符。2.2 Mybatis/MybatisPlus对预编译的封装MybatisMybatisPlus在其之上构建的核心设计之一就是将参数化查询模型化、优雅地集成到了XML映射文件和注解中。在XML映射文件中select idselectUser resultTypeUser SELECT * FROM user WHERE name #{name} /select这里的#{name}就是Mybatis的参数占位符。在运行时Mybatis会将其转换为JDBC的?并通过PreparedStatement.setXxx()方法安全地设置参数值。这是绝对安全的用法。与之相对的危险用法是${}select idselectUser resultTypeUser SELECT * FROM user ORDER BY ${orderByField} /select${orderByField}是字符串替换。Mybatis在运行前会直接将变量的值替换到SQL语句中然后才发送给数据库。如果orderByField来自用户输入且未经验证例如输入name; DROP TABLE user--生成的SQL将是灾难性的。因此${}只能用于拼接非用户输入的、可信的SQL片段如固定的列名、表名但即使如此也需谨慎。MybatisPlus的CRUD接口、条件构造器如QueryWrapper其设计目标就是将开发者从编写原始SQL无论是#{}还是${}中解放出来通过API调用的方式生成最终的安全SQL。接下来我们就深入它的条件构造器看看安全与风险的边界在哪里。3. QueryWrapper与LambdaQueryWrapper安全区的正确打开方式MybatisPlus的条件构造器是其标志性功能之一它让我们能够以面向对象的方式构建查询条件。正确使用时它是坚固的盾牌错误使用时它可能留下缝隙。3.1 安全的方法使用Getter方法引用或字符串常量LambdaQueryWrapper推荐的安全方式LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.eq(User::getName, userInputName) .gt(User::getAge, minAge);User::getName是一个方法引用它在编译时就被确定指向User实体类的getName方法对应的数据库字段默认是下划线格式的name。MybatisPlus在内部处理时会将字段名name和参数值userInputName分开处理字段名作为SQL标识符参数值通过预编译占位符?传入。整个过程用户输入的userInputName没有机会干扰SQL结构。基于字符串的QueryWrapper需注意写法QueryWrapperUser wrapper new QueryWrapper(); wrapper.eq(“name”, userInputName) .gt(“age”, minAge);这里的“name”和“age”是硬编码的字符串列名。只要这个列名字符串不是来自用户输入而是开发者自己写的那么userInputName和minAge作为参数值依然是通过预编译传入的因此也是安全的。风险点在于如果你错误地将列名也变成了动态的String column request.getParameter(“column”); // 危险 wrapper.eq(column, userInputValue);此时column作为SQL的一部分列名来自用户输入就可能被注入。例如用户传入11) OR (11作为column结合某些条件可能构造出意外的查询。3.2 需要高度警惕的“模糊”地带like、in等语句即使使用安全的API在某些特定场景下如果对输入值处理不当也可能间接引发问题尤其是在模糊查询和in语句中。模糊查询like的陷阱wrapper.like(“name”, userInput);假设userInput包含通配符%或_例如用户搜索%那么like ‘%’会匹配所有记录。这不是SQL注入但可能是一个逻辑漏洞导致返回过多数据引发性能问题或数据过度暴露。如果本意是精确匹配包含百分号的字符串就需要对输入进行转义或者在应用层处理。注意MybatisPlus的like方法默认会在值两侧加上%即like %value%。如果你使用wrapper.like(“name”, userInput)而userInput本身包含%那么最终的匹配模式会变得复杂。对于需要由用户控制通配符的场景应使用wrapper.apply不那更危险见下文。更安全的做法是在业务代码中对userInput中的通配符进行转义或过滤或者明确使用wrapper.eq。in语句的构造ListLong idList Arrays.asList(1L, 2L, 3L); wrapper.in(“id”, idList);这是安全的MybatisPlus会生成id in (?, ?, ?)并进行预编译。危险来自于手动拼接in语句的字符串String ids “1,2,3”; // 假设来自用户输入 “1) OR 11 --” wrapper.inSql(“id”, ids);inSql方法会将第二个参数直接拼接到SQL中生成id in (1) OR 11 --)导致注入。绝对不要使用inSql来处理来自用户输入的、逗号分隔的ID字符串。正确的做法是将字符串分割成List再使用安全的in方法。3.3 动态排序的安全实践排序字段和方向order by是另一个常见动态需求且不能使用#{}预编译因为字段名和ASC/DESC是SQL语法的一部分。String orderByField request.getParameter(“orderBy”); // 例如 “name” String orderDirection request.getParameter(“order”); // 例如 “desc”错误做法直接拼接wrapper.orderBy(true, false, orderByField “ “ orderDirection);或使用更危险的last方法wrapper.last(“order by “ orderByField “ “ orderDirection);安全做法白名单校验// 定义允许排序的字段白名单 SetString allowedFields new HashSet(Arrays.asList(“name”, “age”, “create_time”)); // 定义允许的排序方向 SetString allowedDirections new HashSet(Arrays.asList(“asc”, “desc”)); if (allowedFields.contains(orderByField) allowedDirections.contains(orderDirection.toLowerCase())) { wrapper.orderBy(true, false, orderByField “ “ orderDirection); // 或者使用 orderByAsc/orderByDesc 方法组合 if (“asc”.equalsIgnoreCase(orderDirection)) { wrapper.orderByAsc(orderByField); } else { wrapper.orderByDesc(orderByField); } } else { // 使用默认排序或抛出异常 wrapper.orderByDesc(“create_time”); }通过白名单机制确保拼接进order by子句的内容完全在控制范围内从而杜绝注入可能。4. 明确的高危禁区apply、last、exists与自定义SQLMybatisPlus提供了一些非常灵活的方法允许开发者插入自定义的SQL片段。这些方法功能强大但一旦接受了不可信的用户输入就是打开了一道直接通往SQL注入的大门。4.1apply方法最容易被误用的“后门”apply方法的签名为apply(String applySql, Object... values)。它的设计初衷是在WHERE条件中插入一段自定义的SQL片段并对其中的{0}、{1}等占位符用values参数进行字符串替换而非预编译。错误案例重现文章开头提到的线上事故代码是这样的wrapper.apply(“date_format(create_time, ‘%Y%m’) {0}”, userInput);开发者的本意是userInput是“202304”这样的字符串替换{0}后生成date_format(create_time, ‘%Y%m’) ‘202304’。这看起来没问题因为userInput被放在单引号内。但攻击者输入的是202304‘) OR 11 --。 替换后生成的SQL片段为date_format(create_time, ‘%Y%m’) ‘202304‘) OR 11 --’由于--是SQL注释符最终有效的WHERE条件变成了... WHERE (date_format(create_time, ‘%Y%m’) ‘202304‘) OR 1111永远为真导致查询条件失效泄露数据。问题的根源apply方法内部对{0}的处理是简单的字符串替换。虽然userInput被替换到了引号内但攻击者通过提前闭合单引号并添加额外的SQL逻辑就跳出了“数据值”的范畴干涉了SQL语法结构。安全使用apply的建议绝对原则apply的SQL片段模板第一个参数必须完全由开发者控制硬编码在代码中。替换值原则{0}、{1}等占位符所替换的值必须进行严格的校验和过滤。对于日期、数字等类型应先转换为对应的Java类型如LocalDate,Integer。对于字符串如果必须使用要严格限制输入格式如正则匹配^\\d{6}$对于年月并进行转义但转义往往复杂且易漏。优先替代方案考虑是否能用安全的wrapper方法组合实现。例如对于日期范围查询使用wrapper.between(“create_time”, startDate, endDate)。对于复杂的函数比较也许需要在业务层计算好值再用eq或ge、le进行比较。4.2last方法在SQL末尾“埋雷”last方法更直接last(String lastSql)。它会在生成的SQL语句末尾直接拼接lastSql字符串。这通常用于添加order by、limit、for update等子句。高危示例String limitSql “limit “ offset “, “ pageSize; // 如果offset/pageSize来自用户 wrapper.last(limitSql);如果用户传入offset为0; DROP TABLE user --生成的SQL将是SELECT ... FROM user limit 0; DROP TABLE user --。分页参数必须转换为整数类型。另一个常见错误是拼接order bywrapper.last(“order by “ orderBy);这等同于直接将用户输入拼接为SQL语法极度危险。解决方案同第3.3节的白名单校验。4.3exists与notExists方法这两个方法用于构建exists子查询其参数是一个子查询SQL字符串。和apply、last一样如果这个子查询SQL字符串包含了未经验证的用户输入就会导致注入。// 危险 String subQuery “SELECT 1 FROM role WHERE role_id ‘“ userInputRoleId “‘ AND user.id role.user_id”; wrapper.exists(subQuery);应使用参数化方式构建子查询或者确保子查询中的条件值来自可信源或经过严格校验。4.4 自定义SQLSelect注解或XML中的${}在MybatisPlus中你仍然可以使用原生的Mybatis方式编写SQL例如在Mapper方法上使用Select注解或在XML文件中编写。Select(“SELECT * FROM user WHERE ${whereCondition}”) ListUser selectByCondition(Param(“whereCondition”) String whereCondition);这里的${whereCondition}是赤裸裸的字符串替换极度危险。绝对禁止将任何来自用户输入的、未经验证和过滤的内容通过${}拼接到SQL中。即使在XML中使用if test”...”等动态SQL标签其test表达式中的变量是OGNL表达式是安全的。但一旦在SQL文本中使用了${column}风险就出现了。select id”selectBySort” SELECT * FROM user ORDER BY ${sortField} ${sortOrder} /select同样必须对sortField和sortOrder实施白名单校验。5. 深度防御超越框架的代码审计与安全实践依赖MybatisPlus的安全特性只是第一层防御。要构建健壮的应用必须在开发流程和代码习惯上建立深度防御体系。5.1 代码审计中的关键检查点在团队Code Review或使用SAST静态应用安全测试工具时应重点关注以下模式搜索${在XML映射文件中全局搜索${检查每一个使用点。确认被替换的变量如${orderBy}是否来自用户输入。如果来自用户输入必须要有严格的白名单校验逻辑并且该逻辑要在审计路径上清晰可见。搜索.apply(和.last(在Java代码中搜索这些方法调用。检查第一个参数SQL片段字符串是否包含字符串连接操作特别是连接了来自HttpServletRequest、RequestParam、PathVariable等来源的变量。搜索.inSql(确认第二个参数是否为不可控的字符串。通常.inSql应该只用于固定的、小的子查询例如id in (select user_id from dept where id 1)。检查Wrapper的setEntity方法wrapper.setEntity(user)会将实体的所有非空字段作为等于条件。需确保这个实体对象的所有字段值都是可信的特别是当实体对象是从前端反序列化而来时要防止攻击者篡改其他查询字段。5.2 输入验证与参数化思维类型强制转换对于分页参数page, size、ID等在Controller层就将其转换为整数类型。Spring MVC的RequestParam或PathVariable可以配合类型声明自动转换转换失败会抛出异常这比在后端处理字符串安全得多。public PageUser listUsers(RequestParam Integer pageNum, RequestParam Integer pageSize) { ... }内容白名单对于排序字段、分组字段、筛选字段名等必须作为SQL语法一部分的输入建立白名单。白名单应尽可能小并与数据库实际列名对应。业务逻辑校验即使参数通过了语法层面的安全检查也要进行业务逻辑校验。例如查询某个用户的订单时除了传入订单ID还必须在查询条件中强制加入当前登录用户的ID条件防止越权。wrapper.eq(“order_id”, orderId).eq(“user_id”, currentUserId);5.3 使用更安全的工具链MybatisPlus代码生成器使用官方代码生成器生成的Entity、Mapper、Service代码默认使用的是安全的#{}和Lambda表达式。这为项目奠定了良好的安全基础。ORM与原生SQL的权衡对于极度复杂的查询如多表关联、窗口函数有时会觉得MybatisPlus的Wrapper表达起来很吃力从而想退回到写原生XML SQL。此时务必坚持使用#{}。如果#{}无法满足如动态表名、列名那就将动态部分严格限制在白名单内。永远不要因为方便而牺牲安全。启用SQL日志与监控在开发测试环境开启MybatisPlus的SQL日志输出mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl。观察最终执行的SQL语句和参数检查是否有意外的拼接行为。在生产环境可以通过APM工具监控慢SQL异常的、全表扫描的SQL有时可能就是注入攻击成功的信号。6. 实战演练构建一个安全的动态查询接口假设我们需要实现一个用户查询接口支持根据姓名模糊、年龄范围、创建时间范围和指定字段排序。不安全版本的诱惑一个快速但不安全的想法可能是接收一个MapString, Object参数然后遍历Map去动态构造Wrapper。这极易出错且危险。安全版本的设计定义安全的请求参数DTOData public class UserQueryDTO { private String nameLike; // 模糊姓名 private Integer minAge; private Integer maxAge; private LocalDateTime createTimeStart; private LocalDateTime createTimeEnd; private String sortBy “create_time”; // 排序字段有默认值 private String sortOrder “desc”; // 排序方向有默认值 }在Service层进行安全构造Service public class UserService { // 排序字段白名单 private static final SetString ALLOWED_SORT_FIELDS Set.of(“name”, “age”, “create_time”); // 排序方向白名单 private static final SetString ALLOWED_SORT_ORDERS Set.of(“asc”, “desc”); public PageUser queryUsers(UserQueryDTO dto, PageUser page) { LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); // 1. 模糊查询对输入进行通配符转义如果业务需要精确包含%_ if (StringUtils.isNotBlank(dto.getNameLike())) { // 假设我们允许用户使用通配符但为安全起见可以在这里进行转义 // String escapedName escapeSqlWildcard(dto.getNameLike()); // wrapper.like(User::getName, escapedName); // 更常见的做法是我们控制通配符用户输入作为纯文本内容 wrapper.like(User::getName, dto.getNameLike()); } // 2. 范围查询直接使用安全的ge, le, between方法 if (dto.getMinAge() ! null) { wrapper.ge(User::getAge, dto.getMinAge()); } if (dto.getMaxAge() ! null) { wrapper.le(User::getAge, dto.getMaxAge()); } if (dto.getCreateTimeStart() ! null dto.getCreateTimeEnd() ! null) { wrapper.between(User::getCreateTime, dto.getCreateTimeStart(), dto.getCreateTimeEnd()); } // 3. 动态排序使用白名单校验 String sortBy dto.getSortBy(); String sortOrder dto.getSortOrder(); if (!ALLOWED_SORT_FIELDS.contains(sortBy)) { sortBy “create_time”; } if (!ALLOWED_SORT_ORDERS.contains(sortOrder.toLowerCase())) { sortOrder “desc”; } // 根据校验后的字段和方向使用安全的orderBy方法 if (“asc”.equalsIgnoreCase(sortOrder)) { wrapper.orderByAsc(getSortLambda(sortBy)); } else { wrapper.orderByDesc(getSortLambda(sortBy)); } return userMapper.selectPage(page, wrapper); } // 一个辅助方法将字符串字段名转换为Lambda表达式简化版实际可能需要反射 // 这里为了安全我们直接使用条件判断避免反射带来的复杂性和潜在风险。 private SFunctionUser, ? getSortLambda(String sortBy) { switch (sortBy) { case “name”: return User::getName; case “age”: return User::getAge; case “create_time”: default: return User::getCreateTime; } } }这个实现完全避免了字符串拼接所有查询条件值都通过Lambda表达式指向明确的字段并通过预编译传入。排序字段通过白名单和switch-case进行严格限制彻底堵死了SQL注入的可能。它可能没有直接拼接字符串那么“灵活”但换来的却是系统的“坚固”。在安全面前这一点点灵活性的牺牲是绝对必要且值得的。记住框架是你的助手而不是你安全意识的替代品。正确的认知加上严谨的实践才能让你的应用在复杂的网络环境中立于不败之地。

相关新闻

5个理由告诉你为什么Flyctl是云原生开发者的必备神器

5个理由告诉你为什么Flyctl是云原生开发者的必备神器

5个理由告诉你为什么Flyctl是云原生开发者的必备神器 【免费下载链接】flyctl Command line tools for fly.io services 项目地址: https://gitcode.com/gh_mirrors/fl/flyctl Flyctl是Fly.io平台的官方命令行工具,专为云原生应用部署和管理而生。无论你是想…

2026/10/16 3:19:01 阅读更多 →
BMP180气压传感器I2C通信与海拔测量实战指南

BMP180气压传感器I2C通信与海拔测量实战指南

1. 项目缘起:从“气压”到“高度”的探索最近在做一个户外环境监测的小项目,需要实时获取海拔高度数据。市面上常见的GPS模块虽然能提供经纬度和海拔,但在城市峡谷或者室内,其精度和稳定性常常让人头疼。这时候,一个更…

2026/10/1 11:09:16 阅读更多 →
5分钟极速搭建:kohya_ss Docker训练环境终极指南

5分钟极速搭建:kohya_ss Docker训练环境终极指南

5分钟极速搭建:kohya_ss Docker训练环境终极指南 【免费下载链接】kohya_ss 项目地址: https://gitcode.com/GitHub_Trending/ko/kohya_ss 你是否曾经为复杂的AI训练环境配置而头疼?想要快速开始Stable Diffusion模型训练却卡在环境依赖上&#…

2026/10/16 6:43:40 阅读更多 →

最新新闻

绿联DX4600 Docker部署Alist:挂载阿里云盘与115网盘教程

绿联DX4600 Docker部署Alist:挂载阿里云盘与115网盘教程

简介:这份图文教程面向绿联 DX4600 及 DH2600 用户,聚焦在 Docker 环境中部署 Alist 并挂载网盘这一常见需求,适合刚接触 NAS 与容器、希望快速搭建个人云盘聚合入口的入门读者。资源包为单个 PDF 文档,体积约 2.91MB,…

2026/10/17 0:19:51 阅读更多 →
MHT多假设跟踪算法Matlab程序实现:从假设生成到航迹管理

MHT多假设跟踪算法Matlab程序实现:从假设生成到航迹管理

简介:这份资源是MHT(多假设跟踪)算法在Matlab环境下的完整实现程序,面向具备Matlab编程基础、正在研究多目标跟踪的开发者与研究生,用于解决雷达、视频监控等场景中目标诞生、消失、分裂、合并等不确定情况下的跟踪难题…

2026/10/17 0:19:51 阅读更多 →
基于Spring Boot的乐器培训管理系统:从设计到部署全解析

基于Spring Boot的乐器培训管理系统:从设计到部署全解析

最近来找我问毕设系统的人不少,点名要“乐器培训管理系统”的尤其多。原因也简单:Spring Boot 这套技术栈已经成了管理类毕设的主流选择,而乐器培训这个业务场景,比一般的“班级管理系统”更接地气,能写的东西也更多。…

2026/10/17 0:19:51 阅读更多 →
云计算运维认证题库实战:VLAN、LVM、Docker与OpenStack操作详解

云计算运维认证题库实战:VLAN、LVM、Docker与OpenStack操作详解

简介:这份资源是一套面向IT运维、云计算与项目管理方向学习者的选择题练习文档,内容覆盖项目管理流程、金融业务合规、OSI参考模型、VLAN与以太网标准、OpenStack组件、AWS云服务、Docker与Linux命令等核心考点,适合备考相关认证或梳理基础知…

2026/10/17 0:19:51 阅读更多 →
金融服务数据管理实战:从个人记账到团队财务自动化搭建

金融服务数据管理实战:从个人记账到团队财务自动化搭建

做金融服务相关的事情做久了,我最大的体会是:大部分人对“金融服务”这个概念的理解,要么停留在银行柜台,要么停留在炒股软件,反而忽略了它真正高频、高价值的场景——把钱的账算清楚、把风险看清楚、把操作做规范。这…

2026/10/17 0:19:51 阅读更多 →
PAT 1046最短距离题的I/O性能陷阱与前缀和优化

PAT 1046最短距离题的I/O性能陷阱与前缀和优化

1. 这道题不是考算法,是考“读题陷阱”的临场反应看到标题里那个醒目的“21行代码AC”,再配上“1046 Shortest Distance (20 分)”这个编号,第一反应往往是:又一道水题?不就是环形数组求两点间最短路径嘛,前…

2026/10/17 0:18:50 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →