dljd_(013_014)_使用PreparedStatement避免SQL注入攻击
一、使用PreparedStatement来避免SQL注入攻击示例
这里我只提供源码、测试类及结果截图信息、建库/表的语句详见上一集
package edu.aeon.logon; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import edu.aeon.aeonutils.AeonJdbcUtils; /** * [说明]:实现用户登录、并且演示sql注入攻击 造成sql注入攻击的原因 * 1.and和or的优先级(and(可以理解为并且)优先与or(可以理解为或者))其实是构造or的条件来无视and条件 * 2.sql语句的拼接 * 如何解决sql注入攻击? 数据库and 和 or的优先级天生的、我们无法改变 唯一解决思路: * 我们用预编译的PreparedStatement来执行sql语句、这种方式不会去拼接sql语句的 * @author aeon * */ public class UserLogon { /** * 封装键盘输入 * * @return */ public static Map<String, String> ReadBoard() { /** * 键盘扫描器:扫描键盘输入的内容 */ Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String username = scanner.nextLine();// 以\n作为一行的标识来读取一行 System.out.println("请输入密码:"); String password = scanner.nextLine(); Map<String, String> userMap = new HashMap<String, String>(); // 将键盘输入的内容封装到map集合中、并且返回给调用者 userMap.put("username", username); userMap.put("password", password); return userMap; } /** * 用户登录具体实现 * * @return 登录成功标志 true则登录成功、否则失败! */ public static boolean userLogon() { boolean flag = false; Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 连接数据库 connection = AeonJdbcUtils.getMySqlConnection(); // 获取到用户输入的数据 Map<String, String> userMap = ReadBoard(); // 登录sql 这里sql语句要设置的内容、不管什么类型,统统用?占位符 String logonSql = "select * from user where username=? and userpw =?"; // 将sql语句预编译到PreparedStatement对象当中 preparedStatement = connection.prepareStatement(logonSql); preparedStatement.setString(1, userMap.get("username")); preparedStatement.setString(2, userMap.get("password")); System.out.println(logonSql); // 因为sql语句已经被编译到PreparedStatement对象中了,所以此处执行再也不需要sql语句 resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { flag = true; } } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { AeonJdbcUtils.closeDB(resultSet, preparedStatement, connection); } return flag; } /** * 用户登录测试 * * @param args */ public static void main(String[] args) { System.out.println(userLogon() ? "用户登录成功!" : "用户登录失败!"); } }
执行结果截图(我们输入错误的用户名和密码):
执行结果截图(我们输入正确的用户名和相应密码):
执行结果截图(再构造sql注入攻击):
这样我们通过预编译的PreparedStatement执行sql语句来避免了sql注入的攻击。
二、statement 和PreparedStatement的区别:
1.statement需要拼接sql语句,会导致sql注入攻击、而PreparedStatement不需要拼接sql语句,从而避免了sql注入攻击。
2.statement执行多条sql语句的时候都是反复的进行先编译再执行、这样效率低,而PreparedStatement只需编译一次、往后都是执行编译好的sql语句、效率高。
所以以后我们只使用PreparedStement。