首先我们模拟下简单的登录功能:
简单需求:
模拟用户登录的简单实现
业务描述:
程序运行 的时候,提供一个输入的人口,可以使用输入用户名个密码,校验用户名和密码是否合法,数据库中是否存在用户
合法登录成功,否则注册登录
数据准备:
设计数据库表,通常我们使用建模工具:PowerDesigner
接下来上代码:
db.properties配置文件如下:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/user user=root password=guisha
实现如下:
package com.guisha.JDBC.Logn; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Scanner; public class JDBCTest { public static void main(String[] agre) { //获取登录时的用户和密码 Map<String, String> loginInfo = loginInfo(); //获取用户名登录验证 boolean loginSuccess = login(loginInfo); //输出结果验证 System.out.println(loginSuccess ? "登录成功!" : "请注册登录!"); } private static boolean login(Map<String, String> loginInfo) { //登录标记 boolean flag = false; //定义用户名和登录密码 String loginName = loginInfo.get("loginName"); String loginPwa = loginInfo.get("loginPwa"); //使用的接口 Connection conn = null; Statement stan = null; ResultSet result = null; try { InputStream input = JDBCTest.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //装载配置文件 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注册驱动 Class.forName(driver); //从输入字节流中读取属性列表(键和元素对)。输入流采用load(Reader)中指定的简单的面向行的格式,并假定使用ISO 8859-1字符编码。 try { //创建链接 conn = DriverManager.getConnection(url, user, password); //获取数据库操作对象 stan = conn.createStatement(); //执行Sql,获取数据 String sql = "select * from loginname where NAME='" + loginName + "' and NAMEPASS= '" + loginPwa + "' "; // 查询结果集 result = stan.executeQuery(sql); //判断 if (result.next()){ flag = true; } } catch (SQLException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }finally { //释放资源 if (result != null){ try { result.close(); } catch (SQLException e) { e.printStackTrace(); } } //释放资源 if (stan != null){ try { stan.close(); } catch (SQLException e) { e.printStackTrace(); } } //释放资源 if (conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } //返回标识 return flag; } public static Map<String, String> loginInfo() { //接受键盘输入 Scanner scanner = new Scanner(System.in); //输入用户名 System.out.println("请输入用户名:"); String loginName = scanner.nextLine(); //输入密码 System.out.println("请输入密码:"); String loginPwa = scanner.nextLine(); //创建键值对集合 Map<String, String> map = new HashMap<String, String>(); //将数据添加到map集合对象中 map.put("loginName", loginName); map.put("loginPwa", loginPwa); //返回集合对象 return map; } }
我们需要了解SQL注入
以上代码中执行执行SQL部分,这里完成了SQL语句的拼接,而executeQuery这里是将sql语句发送给DBMS进行代码编译
正好将用户提供的"非法信息"编译进去,导致原SQL语句的含义被扭曲,会导不正常的操作变的正常、
导致SQL注入的根本原因是什么?
用户输入的信息中包含SQL语句的关键字,并且这些关键字参与sql语句的编译过程,导致SQL语句原意改变,进而达到SQL注入的效果(不能小视,可能会造成数据库崩溃或者被改变)
解决SQL注入问题?
首先我们知道出现SQL注入的原因是用户在提供的信息包含SQL语句的关键字,并且在传给DBMS预编译时传入了非法的SQL信息,那么,只要用户提供的信息不参与SQL语句的编译过程,就可以解决问题(即使信息中包含SQL语句的关键字,但是不参与预编译,不起作用)。
想要用户信息不参与SQL预编译,就需要使用java.sql.PreparedStatement接口,此接口继承了java.sql.Statement。
PreparedStatement是属于预编译的数据库操作对象,原理是:预先对SQL语句的框架进行预编译,然后再给SQL语句传值
我们对代码进行修改:
package com.guisha.JDBC.Logn; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Scanner; public class JDBCTest { public static void main(String[] agre) { //获取登录时的用户和密码 Map<String, String> loginInfo = loginInfo(); //获取用户名登录验证 boolean loginSuccess = login(loginInfo); //输出结果验证 System.out.println(loginSuccess ? "登录成功!" : "请注册登录!"); } private static boolean login(Map<String, String> loginInfo) { //登录标记 boolean flag = false; //定义用户名和登录密码 String loginName = loginInfo.get("loginName"); String loginPwa = loginInfo.get("loginPwa"); //使用的接口 Connection conn = null; PreparedStatement ps = null; ResultSet result = null; try { InputStream input = JDBCTest.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //装载配置文件 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注册驱动 Class.forName(driver); //从输入字节流中读取属性列表(键和元素对)。输入流采用load(Reader)中指定的简单的面向行的格式,并假定使用ISO 8859-1字符编码。 try { //创建链接 conn = DriverManager.getConnection(url, user, password); //获取数据库操作对象 //SQL语句,其中一个 ? ,表示一个占位符,一个?将来接受一个“值”,注意:占位符不能使用 ' ' 括起来 String sql = "select * from loginname where NAME= ? and NAMEPASS = ? "; ps= conn.prepareStatement(sql); //给占位符串 “值” (第一个? 下标为1,第二个为2,切记 JDBC中所有的下标从1开始) ps.setString(1,loginName); ps.setString(2,loginPwa); //执行Sql,获取数据对语句进行预编译 // 获取结果集 result = ps.executeQuery(); //判断 if (result.next()){ flag = true; } } catch (SQLException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }finally { //释放资源 if (result != null){ try { result.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 flag; } public static Map<String, String> loginInfo() { //接受键盘输入 Scanner scanner = new Scanner(System.in); //输入用户名 System.out.println("请输入用户名:"); String loginName = scanner.nextLine(); //输入密码 System.out.println("请输入密码:"); String loginPwa = scanner.nextLine(); //创建键值对集合 Map<String, String> map = new HashMap<String, String>(); //将数据添加到map集合对象中 map.put("loginName", loginName); map.put("loginPwa", loginPwa); //返回集合对象 return map; } }