JDBC-SQL注入与事务使用

前言

  1. 务必先看完我写的带有MySQL标签的文章与JDBC深入这篇文章
  2. 之前,JDBC深入这篇文章中使用Statement语句执行SQL语句处,我说了会存在SQL注入问题,各大网站在2016年之前都对SQL注入登录的用户进行账号冻结,现在只是提示你账号密码错误。黑客们在洪荒时期可能会使用这种方法攻击某些小网站,非常有效。
  3. 这里,我还想在JDBC连接中,谈谈事务处理的问题,这在实际开发中具有重要作用

SQL注入问题

什么业务会遇到SQL注入问题?

最普遍的就是用户登录功能的实现

登录业务模拟SQL注入

JDBC程序运行的时候,通常会提供一个输入的入口,可以让用户输入用户名和密码,用户输入用户名和密码之后,提交信息,java程序收集到用户信息,Java程序连接数据库验证用户名和密码是否合法

  • 合法:显示登录成功
  • 不合法:显示登录失败

我们来看下面的代码,关键在于Statement的对象来执行select语句

Statement stmt = conn.createStatement();
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
rs = stmt.executeQuery(sql);

以上正好完成了sql语句的拼接,上述代码的含义是:

  • 发送sql语句给DBMS,DBMS进行sql编译。
  • 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了

当前程序存在的问题,如果我们使用下列方式登录,隐患非常大

用户名:随意
密码:随意' or '1'='1 

无论用户名在数据库中是否存在,or '1' = '1'永远为真,从而可以顺利登录

这种现象就是SQL注入

解决方案

我们分析一下为什么会出现SQL注入?

  1. 用户是计算机专业人士
  2. 用户提供的信息“非法”(含有SQL关键字)并参与了SQL语句编译

所以关键在于我们要让用户提供的信息不参与SQL语句的编译过程,这样的话,即使用户提供的信息中含有SQL语句的关键字,但是它没有参与编译,不起作用

而要想用户信息不参与SQL语句的编译,那么我们必须使用java.sql.PreparedStatement

PreparedStatement

这是一个接口,

  • 它继承了java.sql.Statement
  •  它是属于预编译的数据库操作对象
    

原理:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”,完美解决了SQL注入问题

关于PreparedStatement具体使用和代码细节见下

// 获取预编译的数据库操作对象
// SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来。
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
// 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
ps = conn.prepareStatement(sql);
// 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
ps.setString(1, loginName);
ps.setString(2, loginPwd);
// 执行sql
rs = ps.executeQuery();

对比Statement和PreparedStatement

  • Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。
  • Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次,PreparedStatement效率较高一些。
  • PreparedStatement会在编译阶段做类型的安全检查。

JDBC事务使用

JDBC中的事务是自动提交的,什么是自动提交?

  •      只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。
    
  •      但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或者同时失败(比如银行转账)
    

模拟JDBC事务

准备SQL脚本

drop table if exists t_act;
   create table t_act(
       actno int,
       balance double(7,2) // 注意:7表示有效数字的个数,2表示小数位的个数。
   );
   insert into t_act(actno,balance) values(111,20000);
   insert into t_act(actno,balance) values(222,0);
   commit;
   select * from t_act;

JDBC开发最终常用代码

public class JDBCTest11 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
            // 将自动提交机制修改为手动提交
            conn.setAutoCommit(false); // 开启事务

            // 3、获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ?";
            ps = conn.prepareStatement(sql);

            // 给?传值
            ps.setDouble(1, 10000);
            ps.setInt(2, 111);
            int count = ps.executeUpdate();

            //String s = null;
            //s.toString();

            // 给?传值
            ps.setDouble(1, 10000);
            ps.setInt(2, 222);
            count += ps.executeUpdate();

            System.out.println(count == 2 ? "转账成功" : "转账失败");

            // 程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
            conn.commit(); // 提交事务

        } catch (Exception e) {
            // 回滚事务
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 6、释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容