JDBC简单入门以及SQL注入代码讲解

JDBC简单入门以及SQL注入代码教程

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
public class JdbcDemo_01 {
@Test
public void demo01() throws Exception{
// 查询所有的分类信息
// 注意:使用JDBC规范,采用都是 java.sql包下的内容
//1 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2 获得连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/webdb_4", "root", "root");
//3获得语句执行者
Statement st = conn.createStatement();
//4执行SQL语句
ResultSet rs = st.executeQuery("select * from category");
//5处理结果集
while(rs.next()){
// 获得一行数据
Integer cid = rs.getInt("cid");
String cname = rs.getString("cname");
System.out.println(cid + " , " + cname);
}
//6释放资源
rs.close();
st.close();
conn.close();
}
}
解释说明:
获得驱动
1. JDBC规范规定,如果需要连接数据库,必须提供驱动接口的实现类
驱动接口:java.sql.Driver
每一个数据库提供驱动jar 都实现该接口
2. MySQL 提供实现类:com.mysql.jdbc.Driver
源码:public class com.mysql.jdbc.Driver implements java.sql.Driver {
3. JDBC规范提供了,注册实现方式
DriverManager.registerDriver( new com.mysql.jdbc.Driver() );
但,如果遵循上面语句,Java代码与 mysql实现类耦合(直接关系),之后切换数据库将不能进行。
希望提供方案时,只要切换数据驱动,就可以切换使用数据库
4. 通常注册驱动使用标准写法
Class.forName("com.mysql.jdbc.Driver")
1) 使用反射的方式加载指定的类
2) 具体加载的类以字符串(类的全限定名称)体现,内容就可以存放到配置文件中,通过修改配置文件方便切换数据库
3) 一个类被加载到内存,静态代码块将执行,static{ ... }
4) com.mysql.jdbc.Driver 源码分析
static{
java.sql.DriverManager.registerDriver(new Driver());
}
注册驱动注意事项
手动注册驱动,驱动注册了几次?
DriverManager.registerDriver( new com.mysql.jdbc.Driver() ); 
注册了2次
第一次:new Driver() 时,Driver类加载,静态代码块执行,注册一次
第二次:手动注册
//结论:注册驱动
Class.forName("com.mysql.jdbc.Driver");
JDBC提供工具类 DriverManager(驱动管理器) 
getConnection() 通过设置具体参数向不同的数据库创建新的连接
参数1:url ,数据访问路径
参数2:user , 数据库用户名
参数3:password , 数据库密码
url访问路径
格式  jdbc:mysql://ip地址:端口号/数据库名称
例如  jdbc:mysql://localhost:3306/webdb_4
jdbc固定
mysql 表示mysql数据库,一般情况会根据数据库不同而不同
localhost:3306 表示数据具体地址,为默认值,及可以省略
webdb_4 表示数据名称
路径简化版   jdbc:mysql:///webdb_4
通过Connection就可以获得针对不同数据库sql语句的执行对象,常用  createStatement()
通过Statement对象可以执行任意的SQL语句
st.executeUpdate(sql) 执行DML语句(增删改 insert、delete、update) ,返回为整形,表示影响行数。
st.executeQuery(sql) 执行DQL语句(查询 select) ,返回ResultSet结果集对象(查询所有数据)
st.execute(sql)  了解,可以执行任意sql语句。返回为boolean
true,表示执行DQL语句,需要通过ts.getResultSet() 获得查询结果
false,表示DML语句,需要通过 ts.getUpdateCount() 获得影响行数。
移动游标
rs.next();  下一个
rs.previous(); 上一个
获得指定列数据
rs.getXxx(String) ,通过字段名称获得内容
rs.getXxx(Integer) ,通过字段索引号获得内容 
例如 :
rs.getString("cname")    获得指定名称
rs.getDouble(1)	获得第二列
什么是SQL注入:
SQL注入:用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义。
假设有登录案例SQL语句如下:
SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX’  OR ‘a’=’a时,则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’  OR ’a’=’a’;
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。
为此,我们使用PreparedStatement来解决对应的问题。
PreparedStatement预处理对象,处理的每条sql语句中所有的实际参数,都必须使用占位符?替换。
String sql = "select * from user where username = ? and password = ?";
PreparedStatement使用,需要通过以下3步骤完成:
1.	PreparedStatement预处理对象代码:
#获得预处理对象,需要提供已经使用占位符处理后的SQL语句
PreparedStatement psmt = conn.prepareStatement(sql)
2.	设置实际参数
void setXxx(int index, Xxx xx) 将指定参数设置指定类型的值
参数1:index 实际参数序列号,从1开始。
参数2:xxx 实际参数值,xxx表示具体的类型。
例如:
setString(2, "1234") 把SQL语句中第2个位置的占位符?替换成实际参数 "1234"
3.	执行SQL语句:
int executeUpdate(); --执行insert update delete语句.
ResultSet executeQuery(); --执行select语句.
boolean execute(); --执行select返回true 执行其他的语句返回false.
下面演示SQL注入例子
@Test
public void demo02(){
//#演示SQL注入
String username = "jack' #";
String password = "12345";
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "select * from user where username='"+username+"' and password='"+password+"'";
System.out.println(sql);
rs = st.executeQuery(sql);
if(rs.next()){
System.out.println("用户登录");
} else {
System.out.println("不能登录");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, st, rs);
}
}
输出结果是:
@Test
public void demo02(){
//#解决SQL注入
//String username = "jack' #";
String username = "jack";
String password = "1234";
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//1 准备sql语句
String sql = "select * from user where username=? and password=?";
//2 获得预处理对象
psmt = conn.prepareStatement(sql);
//3设置实际参数
psmt.setString(1, username);
psmt.setString(2, password);
//4执行sql语句 , 注意:没有实际参数
rs = psmt.executeQuery();
if(rs.next()){
System.out.println("用户登录");
} else {
System.out.println("不能登录");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, psmt, rs);
}
}
JdbcUtils工具类:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtils {
private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/webdb_4";
private static String user = "root";
private static String password = "root";
static{
try {
//注册驱动
Class.forName(driver);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获得连接
* @return
* @throws SQLException 
*/
public static Connection getConnection() throws  SQLException{
//获得连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void closeResource(Connection conn , Statement st , ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
}
}
if(st != null){
try {
st.close();
} catch (SQLException e) {
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
}
}
}
}
(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐