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();
                }
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容