JDBC编程——SQL注入问题以及解决方案

一、在JDBC 连接数据库方面存在一个问题:SQL注入(安全隐患)

SQL注入即是指应用程序对用户输入数据的合法性没有判断或过滤不严,一些非法攻击者可以在应用程序中事先定义好的查询语句的结尾上添加额外的,导致在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

比如在我们用户登录 的时候,使用到了以下的登录密码:

/*用户名:sa
密码:ds' or '1'='1
登录成功*/
//select * from t_user where loginname='sa'and loginpw='ds' or '1'='1'
这里的密码使用到了SQL语句关键字:or,数据库服务器把它当作了sql语句来执行,导致用户登陆成功。这种现象被称为sql注入(黑客常用)
  • SQl注入安全隐患的根本原因是:

    • 用户输入的信息中含有sql语句的关键字并且这些关键字参与sql语句的编译过程导致sql语句的意愿被扭曲,进而达到sql注入。

二、解决SQL注入问题的方案:

只要用户提供的信息不参与sql语句的编译过程,问题就解决了。   即使用户提供的信息中含有sql语句的关键字,但是没有参与编译,仍然不起作用 。

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

PreparedStatement接口继承了java.sql.Statement

PreparedStatement是属于预编译的数据库对象

PreparedStatement的原理是:预先对SQL语句的框架进行编译。然后再给SQL语句传“值”

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*解决SQL注入问题:
* 测试结果:
*       用户名:sa
        密码:ds' or '1'='1
        登录失败
    */
public class JDBCtest {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String, String> userloginInfo = initUI();
        //验证用户名和密码
        Boolean loginSuccess = login(userloginInfo);
        System.out.println(loginSuccess?"登录成功":"登录失败");
    }
    /**
     * 用户登录
     * @param userLogin 用户登录信息
     * @return false表示失败,true表示成功,
     */
    private static Boolean login(Map<String, String> userLogin) {
        //标识变量
        Boolean flag =false;
        //JDBC代码
        ResultSet rs = null;
        PreparedStatement ps = null;
        Connection conn = null;
        //单独定义变量
        String loginname = userLogin.get("loginname");//获取用户输入的Map集合里面的“key”
        String loginpw = userLogin.get("loginpw");
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/sql", "root", "123456");
            //3获取预编译的数据库操作对象
//SQL语句的框架这,两个?我们称为”占位符“,一个?表示一个占位符,一个?只能接受一个值,注意:?不能用单引号括起来。
            String dml = "select * from t_user where loginname=? and loginpw=?";
            //程序执行到此处,会发送SQL语句框架给DBMS,然后DBMS会进行对SQL语句的预编译
            ps = conn.prepareStatement(dml);
            //编译完成之后,就要对SQL语句“占位符?”进行传值(只编译一次)
            //(第一个?下标是1,第二个?下标是2,JDBC中所有的下标都是从1开始的.
            ps.setString(1,loginname);
            ps.setString(2,loginpw);
            //4、执行sql
            rs = ps.executeQuery();
            //5处理结果集
            if (rs.next()){
                flag= true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                try {
                    if (ps!= null) {
                        ps.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            return flag;
        }
    }
        /**
         * 初始化用户界面
         * @return 输入用户名和密码等信息;
         */
        private static Map<String, String> initUI () {
            Scanner s = new Scanner(System.in);
            System.out.print("用户名:");
            String loginname = s.nextLine();
            System.out.print("密码:");
            String loginpw = s.nextLine();
            //把用户名和密码组装到Map集合中去:
            Map<String, String> userloginMap = new HashMap<>();
            userloginMap.put("loginname", loginname);
            userloginMap.put("loginpw", loginpw);
            return userloginMap;
        }
}

三、Statement和preparedStatement的对比

  • Statement存在SQl注入问题,preparedStatement解决了该问题

  • Statement是编译一次执行一次,preparedStatement是编译一次可执行N次,preparedStatement效率较高

  • preparedStatement会在编译阶段做类型的安全检查。

综上所述,preparedStatement使用较多,Statement极少数的情况下才使用。

2、那么什么情况下必须使用Statement呢?

  • 就是业务方面必须要求支持sql注入的时候。,进行SQl语句拼接的,必须使用Statement。

  • 例如:以下的程序给表中的数据排序,要用到SQL语句的拼接。

import java.sql.*;
import java.util.Scanner;
public class JDBCtest {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("输入desc或asc,desc表示降序,asc表示升序:");
        String d = s.nextLine();
//JDBC代码
        ResultSet rs = null;
        Statement statement = null;
        Connection conn = null;
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/sql", "root", "123456");
            //3获取数据库操作对象
            statement = conn.createStatement();
            //4、执行sql
            String dml = "select username  from t_student order by username "+d;
            rs = statement.executeQuery(dml);
            //5处理结果集
            while (rs.next()) {
                System.out.println(rs.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                try {
                    if (statement != null) {
                        statement.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
输出结果:
/*输入desc或asc,desc表示降序,asc表示升序:
  asc
        jack
        ls
        rose
        zs
*/

好了,以上就是我对SQL注入问题的解决方案,与大家一起共享,希望对大家可能有一定的帮助

你的问题得到解决了吗?欢迎在评论区留言,提出更好的意见和建议 。  

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-今非昔比°

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值