@Aspect 不适合直接拦截 SQL 注入,因其执行时机在方法调用层面(如 @Service/@Repository),此时参数已进入业务逻辑甚至被拼接成 SQL 字符串;而 SQL 注入始于用户输入,必须在请求到达 Controller 前、通过 Filter 在 Servlet 入口统一清洗或拦截原始参数、body 和 header,才能有效防御。

为什么 @Aspect 不适合直接拦截SQL注入
切面编程(@Aspect)在 Spring Boot 2.7 中无法可靠拦截 SQL 注入,因为它的执行时机太晚——它作用于方法调用层面(如 @Service 或 @Repository 方法),而此时参数早已进入业务逻辑,甚至可能已被拼接到 SQL 字符串中。如果 DAO 层用了 ${}、String.format() 或手动拼接 WHERE 条件,@Aspect 拦不到原始请求数据,也改不了已生成的恶意语句。
真正该拦截的位置:HTTP 请求入口
SQL 注入攻击始于用户输入,必须在请求到达 Controller 之前就做清洗或拒绝。Spring Boot 2.7 推荐用 Filter(非 @Aspect)处理,原因很实在:
-
Filter在 Servlet 容器最外层,能拿到原始HttpServletRequest的所有参数、body、header - 可统一处理 GET 查询参数、POST 表单、JSON body(需包装
HttpServletRequestWrapper) - 支持早期中断(
response.sendError(400)),避免后续流程执行 - 不依赖 Spring 上下文初始化顺序,比
@Aspect更稳定
Filter 中怎么检测和清理?关键三步
实际落地时,别只靠正则匹配关键词(比如 "select|union|drop"),那容易误杀(如用户名含 "user_select")。更稳妥的做法是组合策略:
- 对所有字符串型参数(包括 JSON body 解析后的字段值),先做 trim + 非空判断
- 用白名单校验:如 ID 字段只允许数字,邮箱字段走
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ - 对无法白名单的字段(如搜索关键词),用
StringEscapeUtils.escapeSql()(Apache Commons Text 1.10+)转义单引号、分号、注释符等,但注意:这只是辅助手段,不能替代预编译 - 禁止在日志中打印原始参数值(尤其
request.getParameterMap()),防止脱敏失效后泄露攻击载荷
MyBatis 的 #{} 和 ${} 是最后防线,不是过滤器的替代品
即使你加了 Filter,仍必须确保 DAO 层不踩坑:
-
#{}是安全的,MyBatis 底层用PreparedStatement绑定参数,攻击 payload 会被当作文本字面量 -
${}是危险的,它直接字符串替换,哪怕 Filter 放过了,也会触发注入(比如动态表名、ORDER BY ${sortField}) - 若真要动态拼接,必须严格白名单控制:
if ("name".equals(sortField) || "created_time".equals(sortField)) { ... } - JPA 的
@Query同理:用:param命名参数,禁用字符串拼接查询
Filter 和预编译不是二选一,而是前后两道闸门;漏掉任何一道,都可能被绕过。最容易被忽略的是 POST JSON body 的解析时机——Filter 必须配合自定义 HttpServletRequestWrapper 才能读取并重写 body 内容,否则 getInputStream() 只能读一次,后续 Controller 就收不到数据了。











