1.Statement接口实现CRUD
package loey.java1;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 实现功能:
* 1、需求
* 模拟用户登录功能的实现
* 2、业务描述
* 程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
* 用户输入用户名和密码之后,提交信息,java程序收集用户信息
* java程序连接数据库验证用户名和密码是否效
* 合法:显示登录成功
* 不合法:显示登录失败
* 3、数据的准备
* 在实际开发中,表的设计会使用专业的建模工具
* 4.当前程序存在问题:
* 请输入登录名:aaa
* 请输入登录密码:aaa' or '1' = '1
* 登录成功
* 这种现象被称为SQL注入(安全隐患,黑客经常使用
* 5.导致SQL注入的根本原因是什么?
* 用户输入的信息中含SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,
* 导致SQL语句的愿意被扭曲,进而达到SQL注入。
*
*/
public class JDBCTest06 {
public static void main(String[] args) {
Map<String, String> userLoginInfo = initUI();
boolean loginSuccess = loginSuccess(userLoginInfo);
System.out.println(loginSuccess ? "登录成功" : "登录失败");
}
private static boolean loginSuccess(Map<String,String> userLoginInfo){
boolean isSuccess = false;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306" +
"/bjpowernode","root","1127");
//3.获取操作数据库的对象
stmt = conn.createStatement();
//4.执行sql语句
String sql = "Select * from t_user where loginName = '" + userLoginInfo.get("loginName")
+"' and loginPwd = '" + userLoginInfo.get("loginPwd") + "'";
//以上正好完成了SQL语句拼接,以下代码的含义是:发送SQL语句给DBMS,DBMS进行编译SQL语句,正好
// 将用户输入的“非法信息”编译进去,导致了原SQL语句的含义被扭曲了。
rs = stmt.executeQuery(sql);
while(rs.next()){
isSuccess = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return isSuccess;
}
/**
* 用户登录界面
* @return 返回用户输入的登录名和登录密码
*/
private static Map<String,String> initUI(){
HashMap<String, String> userLoginInfo = new HashMap<>();
Scanner scan = new Scanner(System.in);
System.out.print("请输入登录名:");
String loginName = scan.nextLine();
System.out.print("请输入登录密码:");
String loginPwd = scan.nextLine();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
弊端:
- 问题三; Statement没办法操作Blob类型变量
- 问题四:Statement实现批量插入时,效率较低
2.解决SQL注入问题
PreparedStatement和Statement对比?
- Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题
- Statement是编译一次执行一次,PreparedStatement是编译一次,可执行N次,PreparedStatement效率较高一些
- PreparedStatement会在编译阶段做类型的安全检查
什么情况下必须使用Statement呢?
- 业务方面要求必须支持SQL注入的时候
- Statement支持SQL注入,凡是业务方面要求是需要进行SQL语句拼接的,必须使用Statement,比如:用户输入desc表示降序,输入asc表示升序
package loey.java1;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 1.解决SQL注入问题
* 只要用户提供的信息不参与SQL语句的编译,问题就解决了,
* 即使用户提供的信息含SQL语句的关键字,但是没参与编译,不起作用
* 要想用户的信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
* java.sql.PreparedStatement接口继承了java.sql.Statement
* PreparedAtatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。
*
* 请输入登录名:aaa
* 请输入登录密码:aaa' or '1' = '1
* 登录失败
*
* PreparedStatement和Statement对比?
* —Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题
* —Statement是编译一次执行一次,PreparedStatement是编译一次,可执行N次,PreparedStatement效率较高一些
* —PreparedStatement会在编译阶段做类型的安全检查
*
* 5、什么情况下必须使用Statement呢?
* 业务方面要求必须支持SQL注入的时候
* Statement支持SQL注入,凡是业务方面要求是需要进行SQL语句拼接的,必须使用Statement
*
*/
public class JDBCTest07 {
public static void main(String[] args) {
Map<String, String> userLoginInfo = initUI();
boolean loginSuccess = loginSuccess(userLoginInfo);
System.out.println(loginSuccess ? "登录成功" : "登录失败");
}
/**
* 判断用户登录是否成功
*
* @param userLoginInfo 用户输入的登录名和登录密码
* @return true:登录成功 false:登录失败
*/
private static boolean loginSuccess(Map<String, String> userLoginInfo) {
boolean isSuccess = false;
Connection conn = null;
PreparedStatement ps = null;//这里使用预编译的数据库操作对象
ResultSet rs = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306" +
"/bjpowernode", "root", "1127");
//3.获取预编译的数据库操作对象
// sql语句的框架中,一个?,表示一个占位符,一个?将来接收一个"值"。注意:占位符不要用单引号括起来
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
// 程序执行到此处,会发送sql语句框架给DBMS,DBMS对sql语句框架进行预编译。
ps = conn.prepareStatement(sql);
// 给占位符?传值,第一个?的下标是1,第二个?的下标是2(JDBC中下标都从1开始)
ps.setString(1,userLoginInfo.get("loginName"));
ps.setString(2,userLoginInfo.get("loginPwd"));
rs = ps.executeQuery();
while (rs.next()) {
isSuccess = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return isSuccess;
}
/**
* 用户登录界面
*
* @return 返回用户输入的登录名和登录密码
*/
private static Map<String, String> initUI() {
HashMap<String, String> userLoginInfo = new HashMap<>();
Scanner scan = new Scanner(System.in);
System.out.print("请输入登录名:");
String loginName = scan.nextLine();
System.out.print("请输入登录密码:");
String loginPwd = scan.nextLine();
userLoginInfo.put("loginName", loginName);
userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo;
}
}