目录
前言
JDBC是SUN公司制定的一套接口(interface)
1、需求
模拟用户登录功能的实现
2、业务描述
程序运行时,提供一个输入的入口,可以让用户输入用户名和密码。用户输入用户名和密码之后,提交信息,java程序收集到用户信息后,连接数据库验证用户名和密码是否合法。
合法:提示登陆成功
不合法:提示登陆失败
3、数据准备
在实际开发中,表的设计会使用专业的建模工具:power desiger来进行数据库表的设计。
4、编程步骤
1、初始化一个界面提供给用户输入,返回值是用户名和密码
2、验证用户名和密码
5、实现源码
public class Login01 {
public static void main(String[] args) {
/*初始化一个界面*/
Map<String,String> loginIn = inintUI();
/*验证用户名和密码*/
boolean loginSuccess = Login(loginIn);
System.out.println(loginSuccess == true ? "登陆成功" : "登陆失败");
}
/**
* 用户登录
* @param loginIn 用户登录
* @return 返回true表示登陆成功,返回false表示登陆失败
*/
private static boolean Login(Map<String, String> loginIn) {
/*打标记*/
boolean loginSuccess = false;
/*获取配置文件*/
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
/*准备一个连接对象*/
Connection connection = null;
/*准备一个数据库操作对象*/
Statement statement = null;
/*准备一个结果集对象*/
ResultSet resultSet = null;
try {
/*注册驱动*/
Class.forName(bundle.getString("driver"));
/*获取连接*/
connection = DriverManager.getConnection(bundle.getString("url"),bundle.getString("user"),bundle.getString("password"));
/*获取数据库操作对象*/
statement = connection.createStatement();
/*执行SQL*/
String sql1 = "select loginName,loginPwd from t_user where loginName = '"+loginIn.get("loginName")+"' and loginPwd = '"+loginIn.get("loginPwd")+"'";
resultSet = statement.executeQuery(sql1);
/*处理结果集*/
if (resultSet.next()) {
/*登陆成功*/
loginSuccess = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
/*释放资源*/
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面
* @return 返回用户登录的用户名和密码
*/
private static Map<String, String> inintUI() {
/*接受用户输入*/
Scanner s = new Scanner(System.in);
/*提醒输入*/
System.out.println("请输入您的用户名: ");
String uname = s.nextLine();
System.out.println("请输入您的密码: ");
String upassword = s.nextLine();
/*将用户输入放入集合,返回用户输入给JVM*/
Map<String,String> userlogin = new HashMap<>();
userlogin.put("loginName",uname);
userlogin.put("loginPwd",upassword);
return userlogin;
}
}
6、程序存在bug
用户名:fdsa
密码:fdsa’ or ‘1’='1
显示登陆成功,但数据库中并没有这些数据,这种现象被称作SQL注入。
(1)导致这种现象的原因是什么?
Debug查看sql语句:
select loginName,loginPwd from t_user where loginName = 'fdsa' and loginPwd = 'fdsa'or'1'='1
用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。
(2)解决SQL注入
只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。即使用户提供的信息中含有SQL语句的关键字,但只要不参与编译,就不会起作用。要想用户信息不参与SQL语句的编译,那么必须使用java.sql.preparedstatement(预编译的数据库操作对象)
preparedstatement的原理:
预先对SQL语句的框架进行编译,然后再给SQL语句传入值。
preparedstatement的使用:
①先准备一个预编译的数据库操作对象
/*准备一个预编译的数据库操作对象*/
PreparedStatement prestatement = null;
②写出sql语句的框架;调用preparestatement方法传入sql
其中一个?是一个占位符,一个?表示将来接受一个值,注意:占位符不能使用单引号括起来
String sql1 = "select loginName,loginPwd from t_user where loginName = ? and loginPwd = ?";
prestatement = connection.prepareStatement(sql1);
③给占位符传入值
第一个?下标是1,第二个?下标是2。JDBC中所有下标从1开始
//给占位符传入值
prestatement.setString(1,loginIn.get("loginName"));
prestatement.setString(2,loginIn.get("loginPwd"));
resultSet = prestatement.executeQuery();
(3)总结:
解决sql注入的关键是:即使用户传入了sql语句的关键字,只要让这些关键字不参与sql语句的编译,就不会存在sql注入。
7、对比一下statement和preparestatement:
①statement存在sql注入的问题,preparestatement解决了sql注入的问题;
②statement是编译一次执行一次,preparestatement是编译一次执行N次;
③preparestatement会在编译阶段进行类型的安全检查。
综上所述:preparestatement在大多情况下使用,只有极少数需要进行sql注入的情况下才会使用statement。
补充知识点:mysql在查询时,如果两次查询使用了相同的查询语句,第一次会进行编译,第二次就不会编译,会直接调用第一次的查询结果。
什么情况下必须使用statement呢?
业务方面要求必须支持sql注入的时候使用statement,statement支持sql注入,凡是业务方面要求是需要对sql语句进行拼接的,必须使用statement(例:升序降序排列时)。如果只需要传入值得到结果,就使用preparestatement。
例:用户输入desc就是降序,输入asc就是升序排列
public class JDBCTest08 {
public static void main(String[] args) {
/*接受用户输入*/
List<String> inputList = input();
/*执行排序*/
Sorting(inputList);
}
private static void Sorting(List<String> inputList) {
/*获取配置文件*/
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
/*准备连接对象*/
Connection conn = null;
/*准备数据库操作对象*/
Statement statement = null;
/*准备一个结果集对象*/
ResultSet resultSet = null;
try {
/*注册驱动*/
Class.forName(bundle.getString("driver"));
/*建立连接*/
conn = DriverManager.getConnection(bundle.getString("url"),bundle.getString("user"),bundle.getString("password"));
/*获取操作对象*/
statement = conn.createStatement();
/*执行sql*/
String sql1 = "select ename,sal from emp order by sal " + inputList.get(0);
resultSet = statement.executeQuery(sql1);
/*遍历结果集*/
while (resultSet.next()) {
String ename = resultSet.getString("ename");
double sal = resultSet.getDouble("sal");
System.out.println(ename + " " + sal);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
/*释放资源*/
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
private static List<String> input() {
/*接收用户输入*/
Scanner s = new Scanner(System.in);
System.out.println("desc表示降序,asc表示升序");
System.out.println("请输入desc或asc : ");
String in = s.nextLine();
List<String> userInput = new ArrayList<>();
userInput.add(in);
return userInput;
}
}