一.实现功能:
1.解决“应用Statement的登录系统”存在的SQL注入问题
2.用户信息表
+----+-----------+----------+----------+
| id | loginName | loginPwd | realName |
+----+-----------+----------+----------+
| 1 | abc | 123 | 张三 |
| 2 | wwe | 456 | 李四 |
+----+-----------+----------+----------+
二.代码实现:
import java.sql.*; import java.util.*; public class JDBCTest02 { public static void main(String[] args) { //初始化界面(用户输入账号和密码) Map<String,String> userLoginInfo = initUI(); //验证用户名和密码(JDBC) boolean loginSuccess = login(userLoginInfo); //显示结果: System.out.println(loginSuccess==true?"登录成功":"登录失败"); } /** * 验证用户登录信息是否正确 * @param userLoginInfo 用户登录信息 * @return false 表示登录失败,true 表示登录成功 */ private static boolean login(Map<String, String> userLoginInfo) { //打标记(登录结果) boolean loginSuccess = false; Connection connection = null; PreparedStatement ps = null; ResultSet resultSet = null; try { //1、注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2、获取连接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","888"); //3、获取预编译的数据库操作对象 //一个?代表一个占位符,一个?接受一个“值” String sql = "select * from t_user where loginName = ? and loginPwd = ? "; //程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql的编译 ps = connection.prepareStatement(sql); //给占位符传值(第一个?下标是1,第二个?下标是2,JDBC的的下标从1开始) ps.setString(1,userLoginInfo.get("loginName")); ps.setString(2,userLoginInfo.get("loginPwd")); //4、执行sql resultSet = ps.executeQuery(); //5、处理结果集 //不需要while结果集,因为查询结果不是 无 就是 1条记录 if (resultSet.next()){ loginSuccess = true; } } catch (Exception e) { e.printStackTrace(); }finally { //从小到大分别关闭三个资源 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } //最后返回 登录结果(boolean) return loginSuccess; } /** * 初始化用户界面 * @return 用户的用户名和密码(Map) */ private static Map<String, String> initUI() { Scanner s=new Scanner(System.in); System.out.println("用户名:"); String loginName = s.nextLine(); System.out.println("密码:"); String loginPwd = s.nextLine(); //用 HashMap(键值对的方式)存储用户输入的 账号和密码 Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",loginName); userLoginInfo.put("loginPwd",loginPwd); return userLoginInfo; } }
三.效果展示:
1.使用 PreparedStatement 解决了SQL注入问题:
用户名: yyds 密码: yyds' or '1'='1 登录失败 Process finished with exit code 0
2.注意:
1)JDBC代码部分的第三步 --- 获取预编译的数据库操作对象(不同于Statement):
先写好sql语句模型:
String sql = "select * from t_user where loginName = ? and loginPwd = ? ";
发送sql语句框子给DBMS,然后DBMS准备进行sql的编译:
ps = connection.prepareStatement(sql);
给占位符传值:?的值为String就setString,?的值为int就setInt ...
ps.setString(1,userLoginInfo.get("loginName")); ps.setString(2,userLoginInfo.get("loginPwd"));
四.Statement 与 PreparedStatement 对比:
1.Statement 编译一次执行一次,PreparedStatement 编译一次,可执行n次,所以 PreparedStatement 效率较高;
2.PreparedStatement 比 Statement 更加安全;
3.Statement 可以完成根据用户的意愿,通过输入sql语句来实现相应的功能,比如:升序(asc)/降序(desc):
1)假设使用 PreparedStatement:
用户想通过按“账号首字母”升序的方式看到用户的信息,所以向?传入“asc”,但这样运行是会报错的,因为实际执行的sql语句是:
select * from t_user order by loginName 'asc' //那这俩单引号是什么鬼嘛,执行肯定报错啊,所以这时只能选择 Statement
String sql = "select * from t_user order by loginName ?"; ps = connection.prepareStatement(sql); ps.setString(1,"asc"); resultSet = ps.executeQuery();
2)假设使用 Statement:
statement = connection.createStatement(); String sql = "select * from t_user order by loginName asc"; resultSet = statement.executeQuery(sql); while (resultSet.next()) { System.out.println(resultSet.getString("loginName")); }
这样就可以根据 loginName 首字母大小升序的方式来输出 loginName:
abc wwe Process finished with exit code 0