JDBC之验证用户名与密码正确性与sql注入的解决办法

首先我们来看一下判断输入的用户名和密码在数据库中是否存在

public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(selectByUsernamePassword("1825030246", "谢淇名"));
	}
public static boolean selectByUsernamePassword(String xuehao,String xingming)
	{
		Connection con=null;
		Statement stmt=null;
		ResultSet rs=null;
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			String url="jdbc:mysql://localhost:3306/hahahhha?useUnicode=true&characterEncoding=UTF8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT";
			con=DriverManager.getConnection(url,"root","password");
			stmt=con.createStatement();
			String sql="select * from student where xuehao= '"+xuehao+"' and xingming= '"+xingming +"'";//此处的单引号是由于数据库中sql语句字符串需要用单引号括起来
//然后这样写后sql= select * from student
//				  where xuehao='xuehao' and xingming='xingming' 
//其中单引号中的xuehao和xingming为我们的函数参数
			rs=stmt.executeQuery(sql);
			if(rs.next())
				return true;
			else 
				return false;
		} catch (Exception e) {//注意此处改成Exception,这样就可以捕捉各种异常
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null) rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
			try {
				if(stmt!=null) stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
			try {
				if(con!=null) con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return false;//防止前面代码出错而导致前面的return代码未执行,所以再此处加一个返回
	}

以上代码在我测试的样例中会返回true,因为我的数据库中确实存在数据。

但是如果按照这种形式加密的话就算我输入的数据不是我数据库中存在的数据也有办法通过
比如我传入的参数为:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(selectByUsernamePassword("sdshdjsh", "120' or '1'='1"));
	}

这样写过之后,也输出true,这就相当于给我们的密码被破解了??
原因:这样写过之后,我们的sql相当于变成了

select * from student
	where xuehao='sdshdjsh' and xingming='120' or '1'='1'

后面的’1’='1’是恒成立的,不管前面的xuehao和xingming是否正确。此种行为称为sql注入。

但我们肯定是不想要sql注入出现的,因此要避免它,以下为我们的改进办法:

public static void main(String[] args) {
		System.out.println(selectPrepareByUP("1825030196", "刘梦豪"));
		System.out.println(selectPrepareByUP("sdshdjsh", "120' or '1'='1"));
	}

public static boolean selectPrepareByUP(String xuehao,String xingming)
	{
		Connection con=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			String url="jdbc:mysql://localhost:3306/hahahhha?useUnicode=true&characterEncoding=UTF8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT";
			con=DriverManager.getConnection(url,"root","password");
			String sql="select * from student where xuehao=? and xingming=?";//此处固定格式参数用?来表示
			pstmt=con.prepareStatement(sql);//在此处传入sql,下面rs就不用传了			//设置第一列为xuehao,第二列为xingming,它就会用特别的方法解决掉sql注入
			pstmt.setString(1, xuehao);//然后再设置第1个?
			pstmt.setString(2, xingming);//设置第2个?
			rs=pstmt.executeQuery();
			if(rs.next())
				return true;
			else 
				return false;
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null) rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
			try {
				if(pstmt!=null) pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
			try {
				if(con!=null) con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return false;
	}

可以发现两次输出分别为true和false,通过PreparedStatement为我们解决sql注入的问题。

猜你喜欢

转载自blog.csdn.net/henulmh/article/details/105042337