Java中Statement的SQL注入问题

Statement:

在这里插入图片描述
Statement是java连接数据库操作的一个接口,但是现在被prepareStatement取代是因为它存在一个SQL注入问题,那什么是SQL注入呢?本篇带你了解。
在了解SQL注入之前,我们先了解一下JDBC连接数据库的知识。


连接mysql数据库:

获取数据库连接具体需要三要素

要素一: Driver接口实现类
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。

加载驱动:Class.forName(“com.mysql.jdbc.Driver”);
com.mysql.jdbc.Driver这个需要到mysql官网去下载一个,具体下载可以去网上找。下载后把压缩包解压
在这里插入图片描述
创建一个文件后把它复制到里并右击鼠标把它放到库中
在这里插入图片描述

要素二: URL
JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔。 jdbc:子协议:子名称
协议: JDBC URL中的协议总是jdbc ;子协议: 子协议用于标识一个数据库驱动程序;子名称: 一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名)

要素三: 用户名和密码
user,password可以用“属性名=属性值”方式告诉数据库
可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接

当三个要素都准备好后,我们需要可以建立与数据库的连接了

我们可以把这4个基本信息声明在一个配置文件中,通过Properties去获取他们。这样当你想修改数据库时,我们只需要在配置文件中修改就可以了。
(配置文件声明在工程的src目录下)

user=root    //用户名
password=123456    //密码
url=jdbc:mysql://localhost:3306/test   //最后一个是你想要连接的数据库
driverClass=com.mysql.cj.jdbc.Driver    // 这个不用变,mysql是8.0以下把如果连接不上,把cj删了

我们可以把数据库连接和关闭资源写到一个工具类中,方便调用

//数据库的连接操作
public static Connection getConnection() throws Exception {
    	//1.加载配置文件
        InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");//"文件路径"
        Properties pros = new Properties();
        pros.load(is);
        
        //2.读取配置信息
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");

        //3.加载驱动
        Class.forName(driverClass);

        //4.获取连接
        Connection conn = DriverManager.getConnection(url,user,password);
        return conn;
    }
//资源关闭操作
public static void closeResource(Connection conn, Statement ps) {
        try {
            if (ps != null) {
                ps.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

使用Statement操作数据库:

test数据库中我们有一个user_table表,里面存放我们的用户名,密码和余额字段
在这里插入图片描述

现在我们对它进行一个查询操作,当我用户名和密码都输入正确的情况下提示登录成功

我们先写一个登录操作代码,里面存在一些大家可能看不懂的代码不用急,我们这篇主要介绍的是SQL注入问题,看不懂的代码下一篇会说明,这里为了方便大家观看,就把数据库连接和资源关闭也一起写上了,没有调用工具类。

// 使用Statement实现对数据表的查询操作
	public static <T> T get(String sql, Class<T> clazz) {
		T t = null;
		Connection conn = null;
		Statement st = null;
		ResultSet rs = null;
		try {
			// 1.加载配置文件
			InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
			Properties pros = new Properties();
			pros.load(is);
			// 2.读取配置信息
			String user = pros.getProperty("user");
			String password = pros.getProperty("password");
			String url = pros.getProperty("url");
			String driverClass = pros.getProperty("driverClass");
			// 3.加载驱动
			Class.forName(driverClass);
			// 4.获取连接
			conn = DriverManager.getConnection(url, user, password);
			st = conn.createStatement();
			rs = st.executeQuery(sql);
			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			// 获取结果集的列数
			int columnCount = rsmd.getColumnCount();
			if (rs.next()) {
				t = clazz.newInstance();
				for (int i = 0; i < columnCount; i++) {
					// 1. 获取列的别名
					String columnName = rsmd.getColumnLabel(i + 1);
					// 2. 根据列名获取对应数据表中的数据
					Object columnVal = rs.getObject(columnName);
					// 3. 将数据表中得到的数据,封装进对象
					Field field = clazz.getDeclaredField(columnName);
					field.setAccessible(true);
					field.set(t, columnVal);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭资源
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (st != null) {
				try {
					st.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
}

当这个写好后,我们使用Statement对user表进行一次查询

 public static void main(String[] args) {
        //键盘录入用户信息
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入你的用户名:");
        String userName = sc.nextLine();
        System.out.print("请输入你的密码:");
        String password = sc.nextLine();
        //编写SQL语句,Statement的第一个弊端,在这里可以看出它存在拼串操作,繁琐
        String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password+ "'";
        //get是上面写的方法
        User user = get(sql, User.class);
        if (user != null) {
            System.out.println("登陆成功!");
        } else {
            System.out.println("用户名或密码错误!");
        }
    }

现在我们测试一下代码:
在这里插入图片描述
在这里插入图片描述
ok,没有问题,但是,输入这个试试用户名(1’ or ),密码(=‘1’ or ‘1’ = '1)
在这里插入图片描述
嗯?这是怎么会登录成功?这就是SQL注入问题。
分析一波:
看看我们编写的sql语句:
当我们正常编写时,sql语句是:
SELECT USER,PASSWORD FROM user_table WHERE USER = 'AA ’ AND PASSWORD = ‘123456’ ;
它的查询条件是满足user= AA并且password=123456
而当我们输入上面出错的那个时我们的sql语句变成了
SELECT USER ,PASSWORD FROM user_table WHERE USER = ’ 1 ’ OR ’ AND PASSWORD = ’ = ‘1’ OR ‘1’ = ‘1’ ;
我们的查询条件变成了user = 1 =AND PASSWORD = ‘1’ = ‘1’ ,而 ‘1’ = ‘1’ 是恒成立的就是true所以无论如何都会登录成功。
这就是我们说的SQL注入问题,这是致命的,而在PrepareStatement中我们会使用?占位符来对我们需要输入的位置进行占位,就杜绝了这种情况。当然,PrepareStatement与Statement相比优势并不止这一点。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 使用参数化查询(Prepared Statement):这是避免 SQL 注入攻击最常用的方法,它可以防止恶意用户在 SQL 语句插入恶意的 SQL 代码。使用参数化查询时,可以将 SQL 查询语句的参数用占位符(?)代替,然后再将参数值与 SQL 查询语句分别传递给数据库进行处理。 2. 对输入数据进行验证和过滤:在应用程序对用户输入的数据进行验证和过滤,以确保输入数据的合法性和安全性。例如,可以对输入数据进行长度、类型、格式等方面的验证和过滤,以过滤掉潜在的恶意代码。 3. 使用安全的编码方式:在拼接 SQL 语句时,需要使用安全的编码方式,以确保 SQL 语句的特殊字符被正确转义。例如,在拼接字符串时应该使用 escape() 函数或者替换特殊字符等方式进行编码,从而避免 SQL 注入攻击。 4. 限制数据库用户的权限:为了避免 SQL 注入攻击,应该限制数据库用户的权限,仅允许其执行必要的操作。例如,数据库用户应该仅被授权执行 SELECT、INSERT、UPDATE 和 DELETE 等操作,而不允许其执行 DROP 或 TRUNCATE 等危险操作。 5. 使用安全的数据库配置:为了避免 SQL 注入攻击,应该使用安全的数据库配置,例如开启防火墙、关闭不必要的服务、设置密码复杂度等。此外,还应该定期对数据库进行安全审计和风险评估,以及及时更新数据库补丁和安全补丁。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值