怎么防止SQL语句注入

如何防止SQL语句注入
版权:JavaIT学习室_在线Java学习论坛
转载请标明,http://www.javait.org

为了给大家将SQL注入的原理,需要大家具备如下的知识:
(1)掌握基本Swing组件的开发
(2)掌握基本JDBC的开发

此课题的视频我们也为大家做好了。在文章的底部,大家可以看看。详细的SQL注入原理可以参考我们论坛的文章:http://forum.javait.org/viewthread.php?tid=53&extra=page%3D1

1、什么样的SQL语句是SQL注入语句

大家先看如下没有加入SQL注入设计的SQL语句

select * from student where sno = 's001'


根据上面的SQL语句,我们只需要改变一下就能够完成SQL语句的注入

select * from student where sno = 's001' or '1'='1'


2、在编写JDBC程序时候,如何利用上面SQL语句注入,完成输入不存在的学号也可以登录成功???

防止SQL注入的方法:利用JDBC的PreparedStatement来完成

3、代码实现
(1)编写界面窗口
package test;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.SQLException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

/**
 * 
 * @author JavaIT学习室
 *
 */
public class TestFrame extends JFrame {
	private JLabel sno; //学号
	private JTextField sno_t; //学号的文本框
	private JButton b; //登录按钮
	
	public TestFrame() {
		init(); //创建窗口的相关属性,例如:设置窗口的大小
	}
	
	private void init() {
		this.setTitle("防止SQL语句注入危险的程序"); //设置窗口标题
		this.setSize(300, 200); //设置窗口的高和宽
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置窗口的关闭方式
		this.setResizable(false); //设置窗口不可最大化,或是设置窗口不可调整大小
		
		//在放置基本组件的时候一定要注意窗口的布局方式
		this.setLayout(new FlowLayout()); //设置窗口是流式布局
		sno = new JLabel("学号:");
		sno_t = new JTextField(8);
		b = new JButton("登录");
		
		this.getContentPane().add(sno);
		this.getContentPane().add(sno_t);
		this.getContentPane().add(b);
		
		b.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				String s = sno_t.getText(); //返回窗口中输入的学号信息
				try {
					int i = DBUtil.getInstance().check_2(s);
					if (i > 0) {
						JOptionPane.showMessageDialog(null, "登录成功");
					} else {
						JOptionPane.showMessageDialog(null, "登录不成功");
					}
				} catch (SQLException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} //将学号传入到数据库中进行匹配
			} //为按钮添加点击事情
			
		});
		
		this.setVisible(true); //设置窗口可以显示
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new TestFrame();
	}

}


(2)数据库代码
package test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


/**
 * 数据库连接的类,利用JDBC技术完成
 * @author JavaIT学习室
 *
 */
public class DBUtil {
	private static final String USER_NAME = "sa";
	private static final String PASSWORD = "123456";
	private static final String driverclass = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	private static final String url = "jdbc:sqlserver://localhost:1433;DatabaseName=stu";
	
	private static DBUtil db = null; //它是一个单例的对象
	
	private DBUtil() {
		try {
			Class.forName(driverclass); //加载驱动,必须将数据库的驱动包导入到工程中,不然会报错。sqljdbc.jar
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 返回主类DBConnectionUtil的对象,因为构造方法被定义为private,所以在初始化主类对象的时候只能用此方法实现
	 * @return
	 */
	public static DBUtil getInstance() {
		if (db == null) {
			db = new DBUtil();
		}
		return db;
	}
	
	/**
	 * 获取数据库连接Connection的对象
	 * @return
	 */
	public Connection getConnection() {
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(url, USER_NAME, PASSWORD);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 关闭数据库连接对象的方法
	 * @param conn
	 */
	public void close(Connection conn) {
		try {
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 没有防止SQL注入的代码
	 * @return
	 * @throws SQLException 
	 */
	public int check(String sno) throws SQLException {
		String sql = "select count(sno) from student where sno = '"+sno+"'";
		System.out.println(sql);
		Statement stmt = DBUtil.getInstance().getConnection().createStatement();
		ResultSet rs = stmt.executeQuery(sql);
		if (rs != null && rs.next()) { //对执行的SQL语句的结果集进行遍历
			if (rs.getInt(1) > 0) {
				return 1; //确认该学生在数据库中存在
			}
		}
		return 0;
	}
	
	/**
	 * PreparedStatement可以防止SQL注意的接口
	 * @param sno
	 * @return
	 * @throws SQLException
	 */
	public int check_2(String sno) throws SQLException {
		String sql = "select count(sno) from student where sno = ?";
		PreparedStatement ps = DBUtil.getInstance().getConnection().prepareStatement(sql);
		ps.setString(1, sno);
		ResultSet rs = ps.executeQuery();
		if (rs != null && rs.next()) { //对执行的SQL语句的结果集进行遍历
			if (rs.getInt(1) > 0) {
				return 1; //确认该学生在数据库中存在
			}
		}
		return 0;
	}
}