diff --git a/.gitignore b/.gitignore index fb6851a..589c1a1 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ # 忽略.idea文件夹下的项目信息文件 -.idea +.idea/ +# 忽略target文件夹,下面都是classes文件 +target/ diff --git a/JavaPracticeCode.iml b/JavaPracticeCode.iml index 8979886..e825434 100644 --- a/JavaPracticeCode.iml +++ b/JavaPracticeCode.iml @@ -10,6 +10,12 @@ - + + + + + + + \ No newline at end of file diff --git a/README.MD b/README.MD index 71af03a..dc461da 100644 --- a/README.MD +++ b/README.MD @@ -70,3 +70,8 @@ To push the current branch and set the remote as upstream, use ~/JavaPracticeCode git push origin master Everything up-to-date ``` + +#### 项目结构 +[jdbc主目录](src/main/java/com/learn/note/practice/jdbc/)目录下有一些数据库的额使用样例。 +[jdbc测试目录](src/test/java/com/learn/note/practice/jdbc/)文件夹下主要是jdbc的单元测试代码。 +`testLoginValidate`函数测试了sql注入,使用`Statement`不安全,容易造成sql注入攻击。 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 407125c..94e340f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,10 +16,35 @@ - junit - junit - 3.8.1 - test + junit + junit + 4.10 + test + + + mysql + mysql-connector-java + 5.1.21 + + + org.slf4j + slf4j-api + 1.7.5 + + + ch.qos.logback + logback-classic + 1.0.13 + + + ch.qos.logback + logback-core + 1.0.13 + + + com.google.guava + guava + 18.0 diff --git a/src/main/java/com/learn/note/practice/jdbc/README.MD b/src/main/java/com/learn/note/practice/jdbc/README.MD new file mode 100644 index 0000000..2086098 --- /dev/null +++ b/src/main/java/com/learn/note/practice/jdbc/README.MD @@ -0,0 +1,154 @@ +### JDBC使用demo + +需要使用到jdbc的mysql驱动,并且使用到了slf4j日志服务。 +所有依赖的jar包通过maven管理 +在pom.xml文件中配置如下 +```xml + + + junit + junit + 4.10 + test + + + mysql + mysql-connector-java + 5.1.21 + + + org.slf4j + slf4j-api + 1.7.5 + + + ch.qos.logback + logback-classic + 1.0.13 + + + ch.qos.logback + logback-core + 1.0.13 + + + com.google.guava + guava + 18.0 + + +``` +最后需要用的jar包如下 +> +junit:4.10 +mysql-connector-java:5.1.21 +slf4j-api:1.7.5 +logback-classic:1.0.13 +logback-core:1.0.13 +guava:18.0 + +测试用例在test文件夹里`~/src/test/java/com/learn/note/practice/jdbc/dao/MySQLConnectTest.java` + +#### SQL注入攻击 +首先写一个登陆验证的函数`public static boolean loginValidate(Account account)` +```java + /** + * 登陆验证 + * @param account 需要被验证的账户 + * @return 登陆成功返回true + * 登陆失败返回false + */ + public static boolean loginValidate(Account account) { + Connection connection = null; + Statement statement = null; + String sql = "select * from account where user_name='" + account.getUserName() + "' and password='" + account.getPassword() + "'"; + logger.info("登陆验证时查询的sql语句是:{}", sql); + try { + connection = DriverManager.getConnection(DBConfig.URL, DBConfig.USERNAME, DBConfig.PASSWORD); + statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql); + + if (resultSet.next()) { + return true; + } else { + return false; + } + } catch (SQLException e) { + logger.info("获取数据库库连接或者执行sql语句或者获取数据错误", e); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + logger.info("Statement关闭异常", e); + } + } + + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + logger.info("关闭数据库连接异常", e); + } + } + } + return false; + } +``` + +测试代码在`MySQLConnectTest.java`中 +```java + /** + * 测试SQL注入 + */ + @Test + public void testLoginValidate() throws Exception { + List accounts = Lists.newArrayList(); + accounts.add(new Account("admin", "test")); + accounts.add(new Account("lilei", "test")); + accounts.add(new Account("hanmeimei", "test")); + + accounts.add(new Account("admin", "tes")); + accounts.add(new Account("lilei", "test1")); + accounts.add(new Account("hanmeimei", "text")); + + accounts.add(new Account("admin' or '1=1", "tes")); + accounts.add(new Account("lilei' or '1=1", "tes1")); + accounts.add(new Account("hanmeimei' or '1=1", "text")); + + accounts.add(new Account("admin';#", "")); //#是注释符,这一行后面的都注销了 + accounts.add(new Account("lilei';#", "")); + + for (Account account : accounts) { + if (MySQLConnect.loginValidate(account)) { + logger.info("用户[{}]登陆成功!", account.getUserName()); + } else { + logger.info("用户]{}]登陆失败!", account.getUserName()); + } + } + } +``` +每个用户在数据库中的密码都是`test`,看看注入测试结果: +> +17:23:02.454 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='admin' and password='test' +17:23:02.827 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[admin]使用密码[test]登陆成功! +17:23:02.828 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='lilei' and password='test' +17:23:02.850 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[lilei]使用密码[test]登陆成功! +17:23:02.851 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='hanmeimei' and password='test' +17:23:02.873 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[hanmeimei]使用密码[test]登陆成功! +17:23:02.874 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='admin' and password='tes' +17:23:02.907 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[admin]使用密码[tes]登陆失败! +17:23:02.908 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='lilei' and password='test1' +17:23:02.929 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[lilei]使用密码[test1]登陆失败! +17:23:02.929 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='hanmeimei' and password='text' +17:23:02.948 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[hanmeimei]使用密码[text]登陆失败! +17:23:02.949 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='admin' or '1=1' and password='tes' +17:23:02.968 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[admin' or '1=1]使用密码[tes]登陆成功! +17:23:02.969 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='lilei' or '1=1' and password='tes1' +17:23:02.998 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[lilei' or '1=1]使用密码[tes1]登陆成功! +17:23:02.999 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='hanmeimei' or '1=1' and password='text' +17:23:03.017 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[hanmeimei' or '1=1]使用密码[text]登陆成功! +17:23:03.018 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='admin';#' and password='' +17:23:03.035 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[admin';#]使用密码[]登陆成功! +17:23:03.035 [main] INFO c.l.n.practice.jdbc.dao.MySQLConnect - 登陆验证时查询的sql语句是:select * from account where user_name='lilei';#' and password='' +17:23:03.052 [main] INFO c.l.n.p.jdbc.dao.MySQLConnectTest - 用户[lilei';#]使用密码[]登陆成功! diff --git a/src/main/java/com/learn/note/practice/jdbc/bean/Account.java b/src/main/java/com/learn/note/practice/jdbc/bean/Account.java new file mode 100644 index 0000000..5e8a57b --- /dev/null +++ b/src/main/java/com/learn/note/practice/jdbc/bean/Account.java @@ -0,0 +1,60 @@ +package com.learn.note.practice.jdbc.bean; + +import java.io.Serializable; + +/** + * @author junqiangshen + * @version v1.0. + * @Time Created on 15-8-3. + */ +public class Account implements Serializable { + + private static final long serialVersionUID = -2650762086865218778L; + private String userName; + private String password; + private int roleType; + + public Account() { + } + + public Account(String userName, String password) { + this.userName = userName; + this.password = password; + } + + public Account(String userName, String password, int roleType) { + this.userName = userName; + this.password = password; + this.roleType = roleType; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getRoleType() { + return roleType; + } + + public void setRoleType(int roleType) { + this.roleType = roleType; + } + + @Override + public String toString() { + return "{ userName:" + userName + "," + "password:" + password + + "roleType:" + roleType + "}"; + } +} diff --git a/src/main/java/com/learn/note/practice/jdbc/dao/DBConfig.java b/src/main/java/com/learn/note/practice/jdbc/dao/DBConfig.java new file mode 100644 index 0000000..b147bc2 --- /dev/null +++ b/src/main/java/com/learn/note/practice/jdbc/dao/DBConfig.java @@ -0,0 +1,15 @@ +package com.learn.note.practice.jdbc.dao; + +/** + * @author junqiangshen + * @version v1.0. + * @Time Created on 15-8-3. + */ +public class DBConfig { + public static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8"; + public static final String USERNAME = "root"; + public static final String PASSWORD = "root0724"; + public static final String GET_ALL_USER = "select * from account"; + public static final String INSERT_ACCOUNT = "insert into account(user_name,password,role_type)values(?,?,?)"; + public static final String GET_ACCOUNT_BY_NAME = "select * from account where user_name =?"; +} diff --git a/src/main/java/com/learn/note/practice/jdbc/dao/MySQLConnect.java b/src/main/java/com/learn/note/practice/jdbc/dao/MySQLConnect.java new file mode 100644 index 0000000..be3ed49 --- /dev/null +++ b/src/main/java/com/learn/note/practice/jdbc/dao/MySQLConnect.java @@ -0,0 +1,105 @@ +package com.learn.note.practice.jdbc.dao; + +import com.google.common.collect.Lists; +import com.learn.note.practice.jdbc.bean.Account; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.*; +import java.util.Collections; +import java.util.List; + +/** + * @author junqiangshen + * @version v1.0. + * @Time Created on 15-8-3. + */ +public class MySQLConnect { + private static Logger logger = LoggerFactory.getLogger(MySQLConnect.class); + + static { + try { + Class.forName("com.mysql.jdbc.Driver").newInstance(); + } catch (ClassNotFoundException e) { + logger.info("数据库驱动加载失败!", e); + } catch (InstantiationException e) { + logger.info("试图通过newInstance()方法实例化异常", e); + } catch (IllegalAccessException e) { + logger.info("安全权限异常", e); + } + } + + public List getAccountList() { + Connection connection = null; + try { + connection = DriverManager.getConnection(DBConfig.URL, DBConfig.USERNAME, DBConfig.PASSWORD); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(DBConfig.GET_ALL_USER); + List result = Lists.newArrayList(); + + while (resultSet.next()) { + String account = resultSet.getString("user_name"); + String password = resultSet.getString("password"); + int roleType = resultSet.getInt("role_type"); + result.add(new Account(account, password, roleType)); + } + statement.close(); + return result; + + } catch (SQLException e) { + logger.info("获取数据库库连接或者执行sql语句或者获取数据错误", e); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + logger.info("关闭数据库连接异常", e); + } + } + } + return Collections.emptyList(); + } + + /** + * 登陆验证 + * @param account 需要被验证的账户 + * @return 登陆成功返回true + * 登陆失败返回false + */ + public static boolean loginValidate(Account account) { + Connection connection = null; + Statement statement = null; + String sql = "select * from account where user_name='" + account.getUserName() + "' and password='" + account.getPassword() + "'"; + logger.info("登陆验证时查询的sql语句是:{}", sql); + try { + connection = DriverManager.getConnection(DBConfig.URL, DBConfig.USERNAME, DBConfig.PASSWORD); + statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql); + + if (resultSet.next()) { + return true; + } else { + return false; + } + } catch (SQLException e) { + logger.info("获取数据库库连接或者执行sql语句或者获取数据错误", e); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + logger.info("Statement关闭异常", e); + } + } + + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + logger.info("关闭数据库连接异常", e); + } + } + } + return false; + } +} diff --git a/src/test/java/com/learn/note/practice/jdbc/dao/MySQLConnectTest.java b/src/test/java/com/learn/note/practice/jdbc/dao/MySQLConnectTest.java new file mode 100644 index 0000000..22e3fd0 --- /dev/null +++ b/src/test/java/com/learn/note/practice/jdbc/dao/MySQLConnectTest.java @@ -0,0 +1,54 @@ +package com.learn.note.practice.jdbc.dao; + +import com.google.common.collect.Lists; +import com.learn.note.practice.jdbc.bean.Account; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static org.junit.Assert.*; + +public class MySQLConnectTest { + private Logger logger = LoggerFactory.getLogger(MySQLConnectTest.class); + + @Test + public void testGetAccountList() throws Exception { + MySQLConnect mySQLConnect = new MySQLConnect(); + List accounts = mySQLConnect.getAccountList(); + for (Account account : accounts) { + logger.info(account.toString()); + } + } + + /** + * 测试SQL注入 + */ + @Test + public void testLoginValidate() throws Exception { + List accounts = Lists.newArrayList(); + accounts.add(new Account("admin", "test")); + accounts.add(new Account("lilei", "test")); + accounts.add(new Account("hanmeimei", "test")); + + accounts.add(new Account("admin", "tes")); + accounts.add(new Account("lilei", "test1")); + accounts.add(new Account("hanmeimei", "text")); + + accounts.add(new Account("admin' or '1=1", "tes")); + accounts.add(new Account("lilei' or '1=1", "tes1")); + accounts.add(new Account("hanmeimei' or '1=1", "text")); + + accounts.add(new Account("admin';#", "")); //#是注释符,这一行后面的都注销了 + accounts.add(new Account("lilei';#", "")); + + for (Account account : accounts) { + if (MySQLConnect.loginValidate(account)) { + logger.info("用户[{}]使用密码[{}]登陆成功!", account.getUserName(), account.getPassword()); + } else { + logger.info("用户[{}]使用密码[{}]登陆失败!", account.getUserName(), account.getPassword()); + } + } + } +} \ No newline at end of file