apache shiro 不能直接防御 sql 注入,因其不处理 sql 查询或数据库交互;它仅负责权限控制(如 subject.ispermitted),而 sql 注入发生在数据访问层,需靠 preparedstatement、orm 参数化查询等手段防护。

Apache Shiro 本身不处理 SQL 查询,也不参与数据库交互,因此它**不能直接防御 SQL 注入**。把防御责任交给 Shiro,就像让门卫去检查厨房里的食材是否变质——职责错位,注定失效。
Shiro 的权限控制和 SQL 注入是两层完全独立的问题
Shiro 负责的是「谁可以访问哪个 URL 或执行哪个操作」,比如 subject.isPermitted("user:delete") 判断当前用户有没有删用户的权限。它校验的是请求上下文(如角色、权限字符串、会话状态),从不接触 SQL 语句、不解析参数、不连接数据库。
而 SQL 注入发生在数据访问层:当 PHP 的 mysql_query()、Java 的 Statement.execute() 或 MyBatis 的未配置 #{} 拼接方式,把未经处理的用户输入直接塞进 SQL 字符串时,漏洞就已形成。
- Shiro 拦得住一个没权限的请求,但拦不住一个有权限的请求里藏了
' OR '1'='1 - Shiro 可以拒绝
/admin/drop-tables,但无法阻止/user/profile?id=123' UNION SELECT password FROM users-- - 即使你用 Shiro 把所有接口都加了
authc拦截,只要后端代码用了String sql = "SELECT * FROM user WHERE id = " + request.getParameter("id"),照样被注入
为什么有人误以为 Shiro 能防 SQL 注入
常见混淆点来自两个场景:
-
参数在 Shiro 过滤链中被提前截断或重写:比如自定义
Filter对request.getParameter()做了全局 trim() 或正则替换,但这不是 Shiro 的行为,而是你写的额外逻辑 -
Shiro Realm 中做了数据库查询且用了拼接 SQL:例如
MyRealm.doGetAuthenticationInfo()里手写了 JDBC 拼接,此时漏洞就在 Realm 里,Shiro 只是“背锅”的执行容器
这类问题必须回归到 Realm 实现本身——要么改用 PreparedStatement,要么换成 JPA/Hibernate 的参数化查询,而不是指望 SecurityManager 自动消毒。
Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
Shiro 可以协同防御的真实切入点
虽然不碰 SQL,Shiro 能通过间接手段提高攻击成本、限制影响范围:
- 强制登录态校验,避免未授权用户触发高危查询入口(如后台导出接口)
- 用细粒度权限(如
data:export:csv)替代粗放式 URL 拦截,让攻击者即使拿到 token,也无法调用敏感数据导出功能 - 结合
Subject.getPrincipals()在 DAO 层注入当前用户 ID,实现「租户隔离」或「数据行级权限」,使注入结果天然受限(例如注入后只能查到自己名下的记录) - 开启 Shiro 的
cacheManager并缓存认证/授权结果,降低因高频探测引发的数据库连接耗尽风险(属于 DoS 缓解,非注入防护)
注意:cacheManager 不会缓存 SQL 查询结果,它只缓存 Shiro 自己的权限决策过程。
真正该做且必须做的三件事
别在 Shiro 配置里找 SQL 注入开关。以下动作才决定你系统是否扛得住:
- 所有数据库访问层统一使用
PreparedStatement(Java)、PDO::prepare()(PHP)或 ORM 的参数占位符(如 MyBatis#{id}、Hibernate:id) - 禁用 MySQL 的
LOAD_FILE、INTO OUTFILE权限;应用数据库账号仅授予SELECT/INSERT/UPDATE/DELETE,明确拒掉DROP/CREATE/ALTER - 在 Controller/Service 层对参数做类型强转与白名单校验:整数用
Integer.parseInt()或filter_var($id, FILTER_VALIDATE_INT),排序字段用in_array($sort, ['name', 'created_at'])
Shiro 的配置文件(如 shiro.ini 或 ShiroConfig.java)里,永远不该出现任何跟 SQL、数据库驱动、字符集或转义逻辑相关的内容——那不是它的地盘。









