简介
通常在java开发过程中,凡是涉及到数据库数据的操作都会存在sql语句拼接的问题,而拼接的sql语句往往会造成注入漏洞,所以为了防止注入的产生,在开发过程中都应该注意这一点。
正文
在java数据库拼接的过程中,为了防止注入的产生,一般我们会使用PreparedStatement对象来解决这个问题,这里以mysql数据库作为主要对象!
PreparedStatement:执行sql的对象:
SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
1、输入用户随便,输入密码:a' or 'a' = 'a
2、sql:select * from user where username = 'admin' and password = '123456' or 'a' = 'a'
- 上面的sql语句放在mysql中会执行查询所有username和password的内容。
通常情况下为了解决注入问题, 开发过程中会使用PreparedStatement对象来解决问题,即:预编译的SQL语句,参数使用?作为占位符。
PreparedStatement对象使用步骤:
1、导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2、注册驱动
3、获取数据库连接对象 Connection
4、定义sql
- 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
5、获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
6、给?赋值:
- 方法: setXxx(参数1,参数2)
- 参数1:?的位置编号 从1 开始
- 参数2:?的值
7、执行sql,接受返回结果,不需要传递sql语句
8、处理结果
9、释放资源
代码演示:
jdbc.properties
url=jdbc:mysql:///数据库名
user=...
password=...
driver=com.mysql.jdbc.Driver // mysql驱动
JDBCUtils.java
package com.test.util;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
*/
static{
//读取资源文件,获取值。
try {
//1. 创建Properties集合类。
Properties pro = new Properties();
//获取src路径下的文件的方式--->ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
//2. 加载文件
pro.load(new FileReader(path));
//3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4. 注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return 连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
* 释放资源
* @param stmt
* @param conn
*/
public static void close(Statement stmt,Connection conn){
if( stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
* @param stmt
* @param conn
*/
public static void close(ResultSet rs,Statement stmt, Connection conn){
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();
}
}
}
}
JDBCDemo.java
package com.test.jdbc;
import com.test.util.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
/**
* 举例:
* 1. 通过键盘录入用户名和密码
* 2. 判断用户是否登录成功
*/
public class JDBCDemo {
public static void main(String[] args) {
//1.键盘录入,接受用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//2.调用方法
boolean flag = new JDBCDemo().login(username, password);
//3.判断结果,输出不同语句
if(flag){
//登录成功
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误!");
}
}
/**
* 登录方法,使用PreparedStatement实现
*/
public boolean login(String username ,String password){
if(username == null || password == null){
return false;
}
//连接数据库判断是否登录成功
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//1.获取连接
try {
conn = JDBCUtils.getConnection();
//2.定义sql
String sql = "select * from user where username = ? and password = ?";
//3.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
//给?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
//4.执行查询,不需要传递sql
rs = pstmt.executeQuery();
//5.判断
return rs.next();//如果有下一行,则返回true
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,pstmt,conn);
}
return false;
}
}
总结
使用PreparedStatement来完成增删改查操作具有如下优点:
1. 可以防止SQL注入
2. 效率更高
评论区