目录
SQL注入问题及解决方案
SQL注入问题
SQL注入问题:利用非法的SQL拼接来达到入侵数据库的目的
示例:
登录查询操作
public class UserDao {
boolean doLogin(String name ,String password) {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/java_test", "root", "******");
System.out.println("连接成功");
Statement statement = connection.createStatement();
//正确的查询语句
String sql = "select * from user where name = '"+name+"' and password = '"+password+"'";
ResultSet resultSet = statement.executeQuery(sql);
if(resultSet.next()) return true;
// 关闭资源
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
System.out.println("连接失败");
e.printStackTrace();
}
return false;
}
传入正确的参数,则返回true,查询成功;
public static void main(String[] args) {
UserDao userDao = new UserDao();
boolean lisi = userDao.doLogin("lisi", "200");
System.out.println(lisi);
}
若参数错误,如密码错误
boolean lisi = userDao.doLogin("lisi", "2000");
若插入错误的SQL语句,即使参数传入错误也能查询成功
String sql = "select * from user where name = '"+name+"' and password = '"+password+"'";
UserDao userDao = new UserDao();
boolean lisi = userDao.doLogin("lisi ’#", "2000");
这就是SQL注入问题了
在上述代码中,是使用用户名和密码来和SQL进行拼接查询数据库,查询操作是需要用户名和密码正确才能执行成功。
本应该通过sql的非法拼接改变SQL语句的语义,就达到了入侵数据库的目的 。
当参数变成 “ lisi ’# ” 时,“‘#”会将后面的内容注释掉,就不在判断passwd是否正确,因此对应登录操作给定用户名,不知道密码情况下也能完成登陆
SQL注入问题解决方案
使用preparedStatement代替Statement
String sql = "select * from user where name = ? and password = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,name);
preparedStatement.setString(2,password);
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()) return true;
boolean lisi = userDao.doLogin("lisi'# ", "2000");
在创建preparedStatement对象时,就将sql语句传入,进行预编译,prepared Statement采用预编译处理机制,SQL和参数分别传递,SQL上参数的位置通过占位符'?'处理;preparedStatement . setString起始位置为1 ,当传入参数时,特殊值就不会被起作用了。
连接池
连接池的工作原理
数据库连接池的基本思想:为数据建立一个连接池,预先在池中放入一定数量的连接,当有数据库操作时,在池中获取一个空闲的连接来支持数据库的操作,当当前的数据库操作完成后,将连接放回池中。
连接池的作用
在JDBC请求MySQL数据库的操作都要进行连接、释放的过程,在并发量大的情况下,频繁的连接和释放势必会消耗系统的性能。可以使用连接复用的方式让来连接重复使用。
连接池的优势
1.资源复用
2.更快的响应速度
3.新的资源分配手段
4.统一的连接关系,避免数据库连接泄露
连接池的使用
1.导入连接
2.参数配置
3.通过连接池获取连接
4.进行SQL操作
以c3p0为例讲解连接池的使用
1.引入c3p0连接
<!--c3p0连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
2.给定c3p0的配置文件 :c3p0-config.xml (名字不能修改)
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--配置连接池mysql-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/ssms</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 初始化连接数 -->
<property name="initialPoolSize">10</property>
<!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime">30</property>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize">100</property>
<!-- 最小连接数 -->
<property name="minPoolSize">10</property>
</named-config>
</c3p0-config>
3.连接池编码
//设置数据源DataSource
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
try {
//获取Connection
Connection connection = dataSource.getConnection();
//获取Statement对象
Statement statement = connection.createStatement();
String sql = "select * from user where id = 25";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
Integer id1 = resultSet.getInt("id");
String account = resultSet.getString("account");
String name = resultSet.getString("name");
System.out.println("Id:" + id1 + ",name:" + name);
}
} catch (SQLException e) {
e.printStackTrace();
}