拦截器通过url解码后检查请求参数(含query string、form data)是否含' or '1'='1、union select等危险模式来识别sql注入特征,但不处理@requestbody json体,也不解析实际sql;需配合contentcachingrequestwrapper避免流重复读,并优先使用druid wallfilter在jdbc层兜底防护。

拦截器里怎么识别可疑的SQL注入特征
Java Web项目中,HandlerInterceptor 本身不解析SQL,它只能对HTTP请求参数做初步筛查。真正要拦住SQL注入,得在参数进入业务逻辑前,检查 request.getParameter()、request.getQueryString() 或 JSON Body 解析后的字符串里是否含典型危险模式:比如 ' OR '1'='1、UNION SELECT、EXEC(、/*、-- 等。注意,不能只靠黑名单关键词匹配——攻击者会用编码绕过(如 %27 代替单引号),所以必须先做 URL 解码再校验。
常见错误是直接对原始 getParameterMap() 做正则扫描,却忽略了 Spring MVC 的 @RequestBody 场景。JSON 请求体不会出现在参数里,得额外从 InputStream 中读取并解析一次(但要注意流只能读一次,需配合 ContentCachingRequestWrapper)。
为什么不该在拦截器里做SQL语句拼接检测
拦截器拿不到 DAO 层实际执行的 SQL,也看不到 MyBatis 的 #{} 和 ${} 区别,更无法判断 JPA 的 @Query 是否用了原生 SQL。试图在拦截器里模拟 SQL 拼接逻辑(比如把参数拼进模板字符串再检查)不仅不可靠,还会引入严重性能损耗和误报——例如用户昵称含 admin'-- 就被拦,而真实漏洞可能藏在 String.format("SELECT * FROM user WHERE id = %s", id) 这种硬编码里,拦截器根本看不见。
真正该做的是:拦截器只负责「参数层」的粗筛;SQL 层面的防护必须交给 PreparedStatement(MyBatis 默认用 #{} 就是走 PreparedStatement)、ORM 框架的查询构造器,或数据库代理(如 Alibaba Druid 的 stat 过滤器)。
如何避免拦截器自身引发空指针或重复读取异常
Spring 的 HttpServletRequest 流默认不可重复读,直接调用 getInputStream() 一次后,后续 Controller 里的 @RequestBody 就会读到空内容。解决方法只有两个:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 用
ContentCachingRequestWrapper包装 request,在preHandle里缓存 body 内容,再交给后续流程 - 只检查 query string 和 form data(即
getParameterMap()),对 JSON 请求放行,靠全局统一的参数校验器(如@Valid+ 自定义注解)补位
另一个坑是忽略字符集:如果前端发来 GBK 编码的请求,而你用 UTF-8 解码,%A3%5C 这类双字节绕过 payload 可能解不出来,导致漏判。建议统一用 request.getCharacterEncoding() 获取实际编码, fallback 到 UTF-8。
Druid 防火墙比自写拦截器更靠谱吗
是的。Druid 的 WallFilter 是在 JDBC 层拦截,能真正看到最终执行的 SQL,支持白名单表名、字段名、函数名,还能配置 selectAllow、deleteAllow 等开关。它不依赖 HTTP 参数,也不怕 JSON 绕过,且已适配大量 MySQL/Oracle 语法变种。
启用方式很简单:在 application.yml 加配置:spring.datasource.druid.wall.enabled=true,再加 spring.datasource.druid.wall.config.select-allow=false 等规则。但要注意——它只对 Druid 数据源生效,且无法拦截非 JDBC 路径(如 JPA native query 直接走 Hibernate Session)。所以最佳实践是:Druid 做 SQL 层兜底,拦截器做参数层初筛,两者不互斥,但别指望一个搞定全部。
最常被忽略的点:开发环境关掉 WallFilter 后,测试时看不出问题,上线才暴露绕过行为;还有人把 selectAllow 设为 false 却忘了放开分页查询所需的 LIMIT,结果所有列表接口全挂。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










