Java JDBC 控制台用户登录测试 及 登录存在SQL注入的解决

实现功能:

1.用户登录

2.java连接数据库验证,控制台显示成功或失败

idea 新建java空项目,新建测试类LoginTest

public static void main(String[] args) {

    //初始化一个界面
    Map<String,String> userLoginInfo = initUI();

    //验证用户名和密码
    boolean loginSuccess = login(userLoginInfo);

    //最后输出结果
    System.out.println(loginSuccess?"登陆成功":"登录失败");
}
private static boolean login(Map<String, String> userLoginInfo) {
 //打标记
    boolean loginSuccess = false;
  //单独定义变量
    String loginName =userLoginInfo.get("loginName");
    String loginPwd = userLoginInfo.get("loginPwd");

   //JDBC代码
    Connection conn =null;
    Statement stmt=null;
    ResultSet re =null;


    try {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取链接
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
        //3.获取数据库操作对象
        stmt = conn.createStatement();
        //4.执行SQL
        String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"' ";
        //以上正好完成了SQL语句的拼接,以下代码的含义是发送SQL语句给DBMS,DBMS进行SQL编译
        re = stmt.executeQuery(sql);//执行查询,将SQL传过来
        //5.处理结果集
        if(re.next()){
            //登陆成功
            loginSuccess = true;

        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        //6.资源释放
        if(re!=null) {
            try {
                re.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(stmt !=null) {
            try {
                re.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null) {
            try {
                re.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    return loginSuccess;
}
private static Map<String, String> initUI() {
    Scanner s = new Scanner(System.in);

    //读取用户名
    System.out.print("用户名:");
    String loginName = s.nextLine();

    //读取密码
    System.out.println("密码:");
    String loginPwd =s.nextLine();

    //将数据组装到Map中
    Map<String,String> userLoginInfo = new HashMap<>();
    userLoginInfo.put("loginName",loginName);
    userLoginInfo.put("loginPwd",loginPwd);

    //返回Map
    return userLoginInfo;
}

执行结果:

登陆成功

存在问题 : SQL注入

用户名:qwer

密码:qwer' or '1'='1

此时能够能录成功,这种现象被称为SQL注入

导致SQL注入的根本原因:

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

其中

String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"' ";

re = stmt.executeQuery(sql);//执行查询,将SQL传过来

上行代码正好完成了SQL语句的拼接,下行代码的含义是发送SQL语句给DBMS,DBMS进行SQL编译,正好将用户提供的“非法信息”编译进去,扭曲了原有SQL语句

 

解决方法:

只要用户提供的信息不参与编译过程,问题就解决了,使用PrepareStatement替换Statement

新建类LoginTest02

public static void main(String[] args) {

    //初始化一个界面
    Map<String,String> userLoginInfo = initUI();

    //验证用户名和密码
    boolean loginSuccess = login(userLoginInfo);

    //最后输出结果
    System.out.println(loginSuccess?"登陆成功":"登录失败");
}

 

private static boolean login(Map<String, String> userLoginInfo) {

    boolean loginSuccess = false;
    //单独定义变量
    String loginName =userLoginInfo.get("loginName");
    String loginPwd = userLoginInfo.get("loginPwd");

    //JDBC代码
    Connection conn =null;
    //这里使用prepareStatement(预编译的数据库操作对象)
    PreparedStatement ps=null;
    ResultSet re =null;


    try {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取链接
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
        //3.获取预编译的数据库操作对象
        String sql = "select * from t_user where loginName = ? and loginPwd = ? ";
        //SQL语句的框子。问号是占位符,不加引号。一号问号接受一个值
        // 程序执行到此处,会发送SQL语句框子给DBMS,然后DBMS进行SQL语句的预先编译
        //给占位符?传值 (第一个问号下标是1,第二个问号下标是2,JDBC所有下标 从1开始)
        //4.执行SQL 
        ps = conn.prepareStatement(sql);
     
        ps.setString(1,loginName);
        ps.setString(2,loginPwd);
             
        re = ps.executeQuery();
        //5.处理结果集
        if(re.next()){
            //登陆成功
            loginSuccess = true;

        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        //6.资源释放
        if(re!=null) {
            try {
                re.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(ps !=null) {
            try {
                re.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null) {
            try {
                re.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    return loginSuccess;
}
private static Map<String, String> initUI() {
    Scanner s = new Scanner(System.in);

    //读取用户名
    System.out.print("用户名:");
    String loginName = s.nextLine();

    //读取密码
    System.out.print("密码:");
    String loginPwd =s.nextLine();

    //将数据组装到Map中
    Map<String,String> userLoginInfo = new HashMap<>();
    userLoginInfo.put("loginName",loginName);
    userLoginInfo.put("loginPwd",loginPwd);

    //返回Map
    return userLoginInfo;
}

执行结果:

SQL注入问题解决,登录正常

解决SQL注入问题的关键:

       用户提供的信息中心即使含有SQL语句的关键字,但这些关键字没有参与编译,只要用户提供的信息不参与编译过程,问题就解决了
       要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
       PreparedStatement接口继承了java.sql.Statement,是属于预编译的数据库操作对象
       原理是预先对SQL语句框架进行编译,然后再给SQL传值

 

进行降序或者升序查询的时候,使用SQL语句拼接,可以使用Statement

posted @ 2021-07-31 16:35  大星星不见了  阅读(235)  评论(0编辑  收藏  举报