Java操作数据库(一,SQL注入与PreparedStatement)

Posted 韶光不负

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java操作数据库(一,SQL注入与PreparedStatement)相关的知识,希望对你有一定的参考价值。

JDBC入门https://blog.csdn.net/weixin_47514459/article/details/121719450

小编相信,通过对上文的阅读,让各位对jdbc(Java对数据库的操作)已经有一定的认识,下面我们就来看看SQL注入的问题与PreparedStatement(对数据库的增删查改)的内容吧!

目录

SQL注入

 模拟用户登录(来方便观看现象)

创建表存储用户

创建登录类

 sql注入

导致sql注入的根本原因:

根本原因:用户提供信息参与了sql语句的编译。

主要因素:程序先进行sql语句的拼接,在进行sql语句的编译,再进行注入

解决sql注入方法

Statement 

PreparedStatement (预编译数据库操作对象)

PreparedStatement的使用(先进行编译,再进行传值,关键字不进行编译了)

修改后全部代码

Statement 与PreparedStatement的比较

Statement(使用,与使用场景)

PreparedStatement(使用与使用场景)


SQL注入

 模拟用户登录(来方便观看现象)

创建表存储用户

CREATE TABLE  t_user( id int primary key auto_increment comment '用户主键',
		user_name varchar(20) not null unique comment '用户名称', 
		user_pwd varchar(20) ,
        real_name varchar(255)
		);
INSERT INTO t_shuihuo VALUES(null,"admin","root","管理员");
INSERT INTO t_shuihuo VALUES(null,"zhangsan","123456","管理员");

创建登录类

public class Jdbc_login 
    public static void main(String[]args)
        //初始化界面,返回用户名与密码
        Map<String ,String> userLogin = initUI();

        //验证用户名与密码
        Boolean ok = checkNameAndPwd(userLogin.get("user_name"),userLogin.get("user_pwd"));

        System.out.println(ok ? "登录成功" : "登陆失败");

    

实现类中方法

 //初始界面,获取用户名和密码
    public static Map<String,String> initUI()
        //存储输入账户,密码
        Map<String,String> map = new HashMap<>();
        System.out.println("***************欢迎登录****************");

        //获取输入输出
        Scanner scanner = new Scanner(System.in);
        System.out.print("用户名:");
        //nextLine接收是接收一行
        String user_name = scanner.nextLine();
        System.out.print("密码: ");
        String user_pwd = scanner.nextLine();

        //将获取的账户密码,输入到集合中
        map.put("user_name",user_name);
        map.put("user_pwd",user_pwd);

        //返回集合
        return map;
    

    //生成用户名与密码检测方法
    private static Boolean checkNameAndPwd(String user_name, String user_pwd) 
        boolean ok = false;//判断登录是否正确

        Connection conn = null;
        Statement stat = null;
        ResultSet res = null;

        try 
            //1,创建驱动
            Class.forName("com.mysql.jdbc.Driver");

            //2,链接数据库
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3333/db_mingzhu?charset=utf-8","root","root");

            //3,获取数据库对象
            stat = conn.createStatement();

            //4,执行sql语句
            String sql = "select * from t_user where user_name = '"+user_name+"' and user_pwd = '"+user_pwd+"'";
            System.out.println(sql);
            res = stat.executeQuery(sql);

            //5,处理查询结果集(判断数据库释放)
            if (res.next())
                ok = true;
            


         catch (Exception e) 
            e.printStackTrace();
        finally 
            //6,释放资源
            if ( res != null )
                try 
                    res.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
            if (stat != null)
                try 
                    stat.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
            if (conn != null)
                try 
                    conn.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
        
        return ok;
    

完整的代码(我懂你的,如果想自己测试,记得修改数据库接口与数据库名称)

package com.luosf.jdbc;

import com.sun.jmx.snmp.SnmpNull;

import java.awt.*;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Jdbc_login 
    public static void main(String[]args)
        //初始化界面,返回用户名与密码
        Map<String ,String> userLogin = initUI();

        //验证用户名与密码
        Boolean ok = checkNameAndPwd(userLogin.get("user_name"),userLogin.get("user_pwd"));

        System.out.println(ok ? "登录成功" : "登陆失败");

    



    //初始界面,获取用户名和密码
    public static Map<String,String> initUI()
        //存储输入账户,密码
        Map<String,String> map = new HashMap<>();
        System.out.println("***************欢迎登录****************");

        //获取输入输出
        Scanner scanner = new Scanner(System.in);
        System.out.print("用户名:");
        //nextLine接收是接收一行
        String user_name = scanner.nextLine();
        System.out.print("密码: ");
        String user_pwd = scanner.nextLine();

        //将获取的账户密码,输入到集合中
        map.put("user_name",user_name);
        map.put("user_pwd",user_pwd);

        //返回集合
        return map;
    

    //生成用户名与密码检测方法
    private static Boolean checkNameAndPwd(String user_name, String user_pwd) 
        boolean ok = false;//判断登录是否正确

        Connection conn = null;
        Statement stat = null;
        ResultSet res = null;

        try 
            //1,创建驱动
            Class.forName("com.mysql.jdbc.Driver");

            //2,链接数据库
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3333/db_mingzhu?charset=utf-8","root","root");

            //3,获取数据库对象
            stat = conn.createStatement();

            //4,执行sql语句
            String sql = "select * from t_user where user_name = '"+user_name+"' and user_pwd = '"+user_pwd+"'";
            System.out.println(sql);
            //程序执行到这,才会将sql语句编译发送给DBMS,DBMS才进行编译
            res = stat.executeQuery(sql);

            //5,处理查询结果集(判断数据库释放)
            if (res.next())
                ok = true;
            


         catch (Exception e) 
            e.printStackTrace();
        finally 
            //6,释放资源
            if ( res != null )
                try 
                    res.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
            if (stat != null)
                try 
                    stat.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
            if (conn != null)
                try 
                    conn.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
        
        return ok;
    

现象

 sql注入

当我们随便输入一个用户名和密码时(让sql语句恒成立)

 发现登录成功了(这种现象称为sql注入)

导致sql注入的根本原因:

当用户输入的信息中含有sql的关键字,在字符串的拼接过程中导致sql语句发生了变化(sql语句恒成立)。

根本原因:用户提供信息参与了sql语句的编译。

主要因素:程序先进行sql语句的拼接,在进行sql语句的编译,再进行注入

解决sql注入方法

Statement 

在Java.sql.statement接口中:先进行字符串的拼接,在进行sql语句的编译

优点:Statement 可以进行sql语句的拼接。

缺点:因为拼接存在,存在sql注入

PreparedStatement (预编译数据库操作对象)

在Java.sql.PreparedStatement 接口中:先进行sql语句的编译,然后在进行sql语句的传值。

优点:避免了sql注入

缺点:不能进行sql的拼接只能传值。

在PreparedStatement的sql语句中一个?表示一个占位符,一个占位符只能接受一个值(如下)

 String sql = "select * from t_user where user_name = ? and user_pwd = ? ";

PreparedStatement的使用(先进行编译,再进行传值,关键字不进行编译了

             PreparedStatement stat = null;  
            //3,获取预编译数据库操作对象
            String sql = "select * from t_user where user_name = ? and user_pwd = ? ";

            //此时发送sql语句给DBMS,进行sql语句的编译
            stat = conn.prepareStatement(sql);

            //给占位符传值
            //JDBC下标从1开始的
            stat.setString(1,user_name); //1,代表第一个问号
            stat.setString(2,user_pwd);  //2,代表第二个问号

            System.out.println(sql);
            //程序执行到这,才会将sql语句编译发送给DBMS,DBMS才进行编译
            res = stat.executeQuery(); //不用在进行sqld的传入了

修改后全部代码

package com.luosf.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Jdbc_sql 
    public static void main(String[]args)
        //初始化界面,返回用户名与密码
        Map<String ,String> userLogin = initUI();

        //验证用户名与密码
        Boolean ok = checkNameAndPwd(userLogin.get("user_name"),userLogin.get("user_pwd"));

        System.out.println(ok ? "登录成功" : "登陆失败");

    



    //初始界面,获取用户名和密码
    public static Map<String,String> initUI()
        //存储输入账户,密码
        Map<String,String> map = new HashMap<>();
        System.out.println("***************欢迎登录****************");

        //获取输入输出
        Scanner scanner = new Scanner(System.in);
        System.out.print("用户名:");
        //nextLine接收是接收一行
        String user_name = scanner.nextLine();
        System.out.print("密码: ");
        String user_pwd = scanner.nextLine();

        //将获取的账户密码,输入到集合中
        map.put("user_name",user_name);
        map.put("user_pwd",user_pwd);

        //返回集合
        return map;
    

    //生成用户名与密码检测方法
    private static Boolean checkNameAndPwd(String user_name, String user_pwd) 
        boolean ok = false;//判断登录是否正确

        Connection conn = null;
        PreparedStatement stat = null;
        ResultSet res = null;

        try 
            //1,创建驱动
            Class.forName("com.mysql.jdbc.Driver");

            //2,链接数据库
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3333/db_mingzhu?charset=utf-8","root","root");

            //3,获取预编译数据库操作对象
            String sql = "select * from t_user where user_name = ? and user_pwd = ? ";

            //此时发送sql语句给DBMS,进行sql语句的编译
            stat = conn.prepareStatement(sql);

            //给占位符传值
            //JDBC下标从1开始的
            stat.setString(1,user_name); //1,代表第一个问号
            stat.setString(2,user_pwd);  //2,代表第二个问号

            System.out.println(sql);
            //程序执行到这,才会将sql语句编译发送给DBMS,DBMS才进行编译
            res = stat.executeQuery();

            //5,处理查询结果集(判断数据库释放)
            if (res.next())
                ok = true;
            


         catch (Exception e) 
            e.printStackTrace();
        finally 
            //6,释放资源
            if ( res != null )
                try 
                    res.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
            if (stat != null)
                try 
                    stat.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
            if (conn != null)
                try 
                    conn.close();
                 catch (SQLException throwables) 
                    throwables.printStackTrace();
                
            
        
        return ok;
    


Statement 与PreparedStatement的比较

关系:Statement是PreparedStatement的父类。

Statement(使用,与使用场景)

进行字符串的拼接

使用场景:(当需要使用关键字(升序或降序)进行拼接时,就使用statement)

PreparedStatement(使用与使用场景)

先编译进行字符串的传值

使用场景(当你进行输入防止sql注入时,就使用PreparedStatement)

以上是关于Java操作数据库(一,SQL注入与PreparedStatement)的主要内容,如果未能解决你的问题,请参考以下文章

Java JDBC概要总结一(基本操作和SQL注入问题)

Prepare 语句的正确顺序以防止用户输入的 SQL 注入

SQL注入与jdbc操作详解

如何动态准备 SQL 查询(也包括列名)避免 SQL 注入

大数据必学Java基础(九十四):SQL注入攻击

java 防止sql注入的方法(非原创)