Java 如何防止 SQL 注入
在日常开发的APP或网站中,你有没有遇到过这样的情况:搜索框里输入“1’ OR 1=1 --”,本来应该只显示自己的订单,结果却跳出了所有用户的信息?这就是SQL注入在搞鬼——它就像有人偷偷溜进了你的数据库,随意查看甚至篡改数据。作为Java开发者,掌握防止SQL注入的方法,就像给数据库装了一道防盗门。
什么是SQL注入?用买菜的例子讲明白
假设你开了一家菜摊,要统计“所有卖西红柿的摊位”。如果用“拼接字符串”的方式,就像老板对着店员喊:“把所有蔬菜是西红柿的摊位找出来!”要是有人故意在“西红柿”里加了“;把土豆摊位也找出来!”,店员就会把所有土豆摊位都列出来——这就是SQL注入。它本质是用户输入的内容被当成了数据库的执行指令,而非普通数据。

最靠谱的方法:用PreparedStatement代替Statement
很多Java新手写SQL时,习惯用Statement拼接字符串,这就给了注入机会。正确的做法是用PreparedStatement,它能把“指令”和“数据”完全分开。举个例子:
如果要根据用户名查用户信息,用Statement会写成:
String sql = "SELECT * FROM user WHERE username = '" + inputUsername + "'";
要是用户输入' OR 1=1 --,SQL就变成SELECT * FROM user WHERE username = '' OR 1=1 --',直接查了所有用户。
用PreparedStatement的话,会写成:
String sql = "SELECT * FROM user WHERE username = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, inputUsername);
这里的?是“占位符”,会告诉数据库:“这部分是用户输入的数据,不是执行指令”,就算输入恶意内容,也只会被当成普通字符串处理,根本没法篡改SQL逻辑。
别忘做输入验证:给数据加个“过滤网”
就算用了PreparedStatement,也不能完全掉以轻心。比如要查手机号,就得先验证输入的是不是11位数字;如果是邮箱,就得检查有没有@符号。就像买水果时,先看是不是坏的,再决定要不要买。
举个例子:如果输入的是手机号,用正则表达式^1[3-9]\d{9}$验证,不符合就直接提示“请输入正确手机号”,连数据库都不用查,自然不会有注入风险。
少用动态SQL,禁用危险权限
如果业务需要用动态SQL(比如根据用户选择筛选商品),别自己拼接字符串,要用框架提供的安全API。比如MyBatis里的#{}占位符,和PreparedStatement的原理一样,能防止注入。另外,别给数据库账号开多余权限,比如只给“查询表”的权限,不给“删除表”的权限,就算真出问题,也能把损失降到最小。
总之,防止SQL注入的核心就是:别把用户输入的内容当成指令,把“指令”和“数据”分开,再给输入内容加个过滤网。这样一来,你的数据库就像装了密码锁的柜子,没人能随便乱翻了。