从 commons.lang 迁移 StringEscapeUtils.escapeSql
Posted
技术标签:
【中文标题】从 commons.lang 迁移 StringEscapeUtils.escapeSql【英文标题】:Migrating StringEscapeUtils.escapeSql from commons.lang 【发布时间】:2015-08-19 13:16:18 【问题描述】:我已经开始将 commons.lang 2 迁移到 commons.lang3。
根据 https://commons.apache.org/proper/commons-lang/article3_0.html
StringEscapeUtils.escapeSql
这是一种误导性的方法,只处理最简单的 SQL 情况。 >由于 SQL 不是 Lang 的重点,所以维护这种方法没有意义。
了解它,但建议使用什么来代替它?
澄清
你能推荐一个第三方来执行类似于StringEscapeUtils.escapeSql的简单escapeSql吗?
【问题讨论】:
描述您的用例。最简单的一种是“运行数据库查询”,为此您通常不需要转义任何 SQL(您可以并且应该使用绑定变量)。 您为什么要这样做?这似乎是个坏主意,所以我可以理解为什么要删除该方法。 简单用例...如果您碰巧在“order by”子句中需要参数,PreparedStatement 将不起作用。 @NathanCrause:嗯。那是什么关系型数据库?可以通过将查询包装到内联视图/子选择中来解决它,以便要排序的表达式成为“列”吗?可能值得为这个特定示例提出一个新问题。 @Thilo 这是一个我没有考虑过的有趣想法。从纯粹的技术角度来看,这似乎是在修补一些不需要修补的东西。要回答第一个问题,那就是 MariaDB。 (这不是自愿的,顺便说一句,我想要 PostgreSQL)。 【参考方案1】:From the Javadocs:
目前这种方法只能把单引号变成双引号("McHale's Navy" => "McHale's Navy")。
这是方法代码:
/**
675 * <p>Escapes the characters in a <code>String</code> to be suitable to pass to
676 * an SQL query.</p>
677 *
678 * <p>For example,
679 * <pre>statement.executeQuery("SELECT * FROM MOVIES WHERE TITLE='" +
680 * StringEscapeUtils.escapeSql("McHale's Navy") +
681 * "'");</pre>
682 * </p>
683 *
684 * <p>At present, this method only turns single-quotes into doubled single-quotes
685 * (<code>"McHale's Navy"</code> => <code>"McHale''s Navy"</code>). It does not
686 * handle the cases of percent (%) or underscore (_) for use in LIKE clauses.</p>
687 *
688 * see http://www.jguru.com/faq/view.jsp?EID=8881
689 * @param str the string to escape, may be null
690 * @return a new String, escaped for SQL, <code>null</code> if null string input
691 */
692 public static String escapeSql(String str)
693 if (str == null)
694 return null;
695
696 return StringUtils.replace(str, "'", "''");
697
因此,您可以通过简单地调用 String#replace
来轻松替换该方法。
但是,删除该方法是有原因的。它真的是半生不熟,我想不出你想要使用它的充分理由。例如,要运行 JDBC 查询,您可以而且应该使用绑定变量,而不是尝试插入和转义字符串文字。
【讨论】:
实际上确实存在一个用例:转义部分无法通过绑定变量进行参数化的 SQL 语句。例如。表名本身:***.com/q/1208442/274677 @MarcusJuniusBrutus:我不会指望那里使用相同的转义规则。如果您在架构对象名称中使用空格或引号之类的东西,您已经在自找麻烦了。 @Thilo 这个用例怎么样:如果你碰巧在“order by”子句中有需要的参数,PreparedStatement 就不起作用。 @NathanCrause 不确定你的意思。如果你想在ORDER BY abc DESC
中有变量列名,那么是的,就像 Marcus 关于表名所说的那样,绑定参数不能用于模式对象名称,只能用于值。
@Thilo 这就是重点——我正在研究的系统有一个“order by”子句,其中包含传入的值(排序顺序基于提供的字符串是否位于开头的评分)或列值的结尾)。所以“我想不出一个好的理由”是一个完全不相关的观点,仅仅因为一个用例不能在某一时刻被理解,并不意味着它没有用例。【参考方案2】:
如果您使用的是 JDBC 连接,请准备一个带有以下参数的语句:
con.prepareStatement("INSERT INTO table1 VALUES (?,?)");
pstmt.setInt(1, 200);
pstmt.setString(2, "Julie");
pstmt.executeUpdate();
您不需要转义使用预准备语句中的函数插入的任何元素。这些会自动转义。
这个问题之前已经回答过: Java - escape string to prevent SQL injection
【讨论】:
但它不适用于所有 SQL 子句。正如我在这里的其他 cmets 中提到的那样,如果您的“order by”中碰巧有参数,它就不起作用。 如果您在需要使用本机查询时使用 JPA/Hibernate,这也适用。请改用准备好的语句。java Query q = entityManager.createQuery("SELECT * FROM people WHERE name = ?") ; q.setParameter(1, "Julie")
【参考方案3】:
OWASP 有一个名为 ESAPI 的 API 提供了其中一些功能,您可以查看一下。
【讨论】:
这里有一个示例和站点内的链接,它解释了如何使用这个 API 以及对其他方法的讨论。 ESAPI.encoder().encodeForSQL(new OracleCodec(), queryparam); cheatsheetseries.owasp.org/cheatsheets/…以上是关于从 commons.lang 迁移 StringEscapeUtils.escapeSql的主要内容,如果未能解决你的问题,请参考以下文章
StringUtils在commons-lang3和commons-lang中的区别
找不org.apache.commons.lang3.builder.EqualsBuilder和commons-lang下载链接
java.lang.ClassNotFoundException: org.apache.commons.lang.exception.NestableRuntimeException