在刚刚学习jsp的时候,总是会遇到一些比较简单的sql注入的语句,例如or ‘1’ = ‘1’这种代码。从mysql里面解释的话,就是mysql拥有where = true就全部数据返回的功能,不过不太清楚这个设计。所以新手是提交容易遇到注入的事情。而且新手最常见的一个现象就是代码的重复度太过于高了,所以一份好的代码,应该是将重复的部分都结构化让其调用,这样子代码更简洁,找错误也不会这里一块那里一块的。
这里面的代码优化其实就是使用Statement的继承对象prepareStatement,这个对象可以进行一些简单的sql注入过滤,这样子就不会有这么低级的漏洞出现了。
这里的公共代码如下:
未过滤前的代码:
package com.d.cn.test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
import com.d.cn.dao.entity.User;
import com.d.cn.dao.utils.JdbcUtil;
public class Login {
/*
* 获取数据库信息函数
*
* */
public User getUserMsg(User user){
User ResultUser = null;
Connection conn = null;
ResultSet rs = null;
Statement state = null;
try {
conn = JdbcUtil.getConnection();
String sqlAll = "SELECT id ,name , password FROM `user` WHERE name='"+user.getName()+"' AND password='"+user.getPassword()+"'";
//用statement接口发送sql语句并执行,执行后的结果集放在ResultSet
state = conn.createStatement();
rs = state.executeQuery(sqlAll);
//处理结果
while (rs.next()) {
ResultUser = new User();
ResultUser.setId(rs.getInt("id"));
ResultUser.setName(rs.getString("name"));
ResultUser.setPassword(rs.getString("password"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.print("错误了");
e.printStackTrace();
}finally{
JdbcUtil.closeAll(conn, state, rs);
}
return ResultUser;
}
//业务层
public User loginService(User user){
System.out.println("业务方法被调用!");
System.out.println("接着调用数据库打交道的方法!");
return this.getUserMsg(user);
}
//用户交互层
public void loginView(){
Scanner input = new Scanner(System.in);
System.out.print("输入用户名:");
String name = input.next();
System.out.print("输入密码:");
String password = input.next();
User user = new User(0,name,password);
User resultUser = this.loginService(user);
if(resultUser != null){
System.out.print("登录成功!!!");
}else{
System.out.print("登录失败!!!");
}
}
public static void main(String[] args) {
Login login = new Login();
login.loginView();
}
}
过滤后:
这个新函数是可以直接将sql的变量直接使用?来表示,之后再调用其的setObject函数来对问号的哪些变量赋值,这样子就能够将数据分离起来了,在setObject函数也可以写一些过滤sql注入的规则。
package com.d.cn.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
import com.d.cn.dao.entity.User;
import com.d.cn.dao.utils.JdbcUtil;
public class Login2 {
/*
* 获取数据库信息函数
*
* */
public User getUserMsg(User user){
User ResultUser = null;
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
/*使用prepareStatement对数据进行处理
* 1.性能相对Statement有所提高
* 2.防止sql的简单注入 1=1
* 3.代码更加直观
*/
String sqlAll = "SELECT id ,name , password FROM `user` WHERE name=? AND password=?";
//用statement接口发送sql语句并执行,执行后的结果集放在ResultSet
ps = conn.prepareStatement(sqlAll);
ps.setObject(1, user.getName());
ps.setObject(2, user.getPassword());
rs = ps.executeQuery();
//处理结果
while (rs.next()) {
ResultUser = new User();
ResultUser.setId(rs.getInt("id"));
ResultUser.setName(rs.getString("name"));
ResultUser.setPassword(rs.getString("password"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.print("错误了");
e.printStackTrace();
}finally{
JdbcUtil.closeAll(conn, ps, rs);
}
return ResultUser;
}
//业务层
public User loginService(User user){
System.out.println("业务方法被调用!");
System.out.println("接着调用数据库打交道的方法!");
return this.getUserMsg(user);
}
//用户交互层
public void loginView(){
Scanner input = new Scanner(System.in);
System.out.print("输入用户名:");
String name = input.next();
System.out.print("输入密码:");
String password = input.next();
User user = new User(0,name,password);
User resultUser = this.loginService(user);
if(resultUser != null){
System.out.print("登录成功!!!");
}else{
System.out.print("登录失败!!!");
}
}
public static void main(String[] args) {
Login2 login = new Login2();
login.loginView();
}
}
上面的代码中的JdbcUtil其实还没有做到最简的操作,所以代码上存在着许多重复代码,增加维护的难度。所以下面是对代码的优化(未优化的代码在我的上一篇文章可以找到):
package com.d.cn.dao.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.d.cn.dao.entity.User;
public class JdbcUtil {
public static Connection conn = null;
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest","root","root");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public static void closeAll(Connection conn,Statement state,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.print("错误了");
e.printStackTrace();
}
}
if(state!=null){
try {
state.close();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.print("错误了");
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.print("错误了");
e.printStackTrace();
}
}
}
//重复代码提取出来,只是sql和sql的参数是不同的,所以可以提取这两个变量出来
public static int commonExcue(String sql,Object[] object){
int row = -1;
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < object.length; i++) {
ps.setObject(i+1, object[i]);
}
row = ps.executeUpdate();
}catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JdbcUtil.closeAll(conn, ps, null);
}
return row;
}
}
用来测试上面的代码:
package com.d.cn.test;
import java.util.List;
import java.util.Scanner;
import com.d.cn.dao.entity.User;
import com.d.cn.test.jdbcCrud;
public class test {
public static void main(String[] args) {
// //查询
// jdbcCrud jdbcCrud = new jdbcCrud();
// List<User> users = jdbcCrud.listAll();
// System.out.println("编号\t姓名\t密码\t");
// for (User user : users) {
// System.out.println(user.getId()+"\t"+user.getName()+"\t"+user.getPassword());
// }
// //添加数据
// jdbcCrud jdbcCrud = new jdbcCrud();
// int flag = jdbcCrud.insert(new User(3,"六六","123"));
// if(flag==1){
// System.out.println("添加数据成功!");
// }else{
// System.out.println("添加数据失败!");
// }
删除数据
// jdbcCrud jdbcCrud = new jdbcCrud();
// int flag = jdbcCrud.delete(new User(3,"",""));
// if(flag==1){
// System.out.println("删除数据成功!");
// }else{
// System.out.println("删除数据失败!");
// }
修改数据
// jdbcCrud jdbcCrud = new jdbcCrud();
// int flag = jdbcCrud.update(new User(3,"六六1","3333"));
// if(flag==1){
// System.out.println("修改数据成功!");
// }else{
// System.out.println("修改数据失败!");
// }
// // 修改数据
// jdbcCrud jdbcCrud = new jdbcCrud();
// Scanner Input = new Scanner(System.in);
// System.out.print("请输入登录用户名:");
// String name = Input.next();
// System.out.print("请输入登录密码:");
// String password = Input.next();
// User ResultUser = jdbcCrud.getUserMsg(new User(0,name,password));
// if(ResultUser!=null){
// System.out.println("登录成功!");
// }else{
// System.out.println("登录失败!");
// }
}
}
因为上面修改的只是一个封装的函数,所以这个测试类基本和上一篇的代码是一样的,毕竟只是对代码内部上添加了一个静态方法来去除重复代码。