可以使用连接来创建您的字符串。这样做不会导致安全警告。在处理较长的 SQL 语句时,为了清楚起见,最好将其拆分为多行
使用变量构造字符串是导致安全警告的原因。
这将导致警告:
String columnName = getName();
String tableName = getTableName();
final String sql = "SELECT MAX(" + columnName + ") FROM " + tableName;
PreparedStatement ps = connection.prepareStatement(sql);
这不起作用:
String columnName = getName();
String tableName = getTableName();
final String sql = "SELECT MAX(" + "?" + ")" +
"FROM " + "?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, columnName);
ps.setString(2, tableName);
它不起作用,因为准备好的语句只允许将参数绑定到 SQL 语句的“值”位。
这是有效的解决方案:
private static final boolean USE_TEST_TABLE = true;
private static final boolean USE_RESTRICTED_COL = true;
private static final String TEST_TABLE = "CLIENT_TEST";
private static final String PROD_TABLE = "CLIENT";
private static final String RESTRICTED_COL ="AGE_COLLATED";
private static final String UNRESTRICTED_COL ="AGE";
....................
final String sql = "SELECT MAX(" +
( USE_RESTRICTED_COL ? RESTRICTED_COL : UNRESTRICTED_COL ) + ")" +
"FROM " +
( USE_TEST_TABLE ? TEST_TABLE : PROD_TABLE );
PreparedStatement ps = connectComun.prepareStatement(sql);
但它仅在您必须在编译时已知名称的两个表之间进行选择时才有效。您可以在超过 2 种情况下使用复合三元运算符,但随后它变得不可读。
如果 getName() 或 getTableName() 从不受信任的来源获取名称,则第一种情况可能是安全问题。
如果这些变量之前已经过验证,则很有可能使用变量构建安全的 SQL 语句。这是您的情况,但 FindBugs 无法弄清楚。Findbugs 无法知道哪些来源可信或不可信。
但是,如果您必须使用来自用户或不受信任的输入的列名或表名,那么就没有办法了。您必须使用其他答案中提出的任何方法来验证自己的此类字符串并忽略 Findbugs 警告。
结论:对于这个问题的一般情况,没有完美的解决方案。