• MyBatis(3)


    一)当我们使用#或者$替换整型变量的时候:

    1)针对替换int类型的参数:

     二)针对替换String类型的参数:

    1)当使用#来进行字符串替换的问题,此时查询成功

    2)使用$来进行查询:直接替换,啥查询不出来

    3)使用$进行查询,直接进行更换传递参数:

    4)针对于$加上双引号,查询成功:

    5)SQL注入演示:

    但是使用#就不会发生SQL注入:

    6)使用排序或者是模糊匹配使用$,但是模糊匹配可以使用MyBatis中的Contact函数

    二)$在其他情况下的使用场景

    1)针对学生信息的userID进行排序,此时前端传入的是一个SQL的关键字字段:

    必须在Controller层进行参数校验和参数合法性的判断,不然在Service层无法进行参数的校验,因为参数在Service层直接就进行使用了,因为这里的应用场景是排序,所以针对前端传递的参数值只有两个一个是asc一个是desc,就可以做出合法参数的校验;

    1.1)使用$来替换url中的queryString的变量值:

    1.2)使用#来替换url中的queryString的变量值:

    1.3)总结:

    1)当前端传递的是一个SQL命令或者是关键字的时候,只能使用$,如果此时使用#就会认为传递的是一个普通的值,而不是SQL命令,此时就会报错

    2)当不得不使用${}的时候,一定要在业务代码中,针对前端参数进行校验

    2)进行like模糊匹配:希望根据前端传递的字符串进行模糊匹配

    注意:在这种情况下我们该如何针对前端进行传递的参数进行校验呢,因为不像之前进行排序的情况一样,query的值是可以在Controller层进行枚举,只有两种取值,desc和asc,但是这种情况下username的值变化多变,我们无法穷举;

     2.1)当使用$针对前端参数username进行替换:成功;

     2.2)当使用#针对前端参数username进行替换:失败;

    2.3)使用contact函数来拼接字符串:况且可以搭配#来进行使用

    三)使用$搭配冒号使用出现SQL注入:

    下面我们来模拟一个登陆场景:用户在浏览器上面输入一个url里面包含着用户名和密码来模拟登陆场景:

    下面是模拟Controller层和Mapper层的代码:

     3.1)当使用#的方式来替换前端传递的字符串:

    3.2)当使用$的方式来替换前端传递的字符串:

    3.3)如果我们非要使用$的话,就必须加上双引号或者单引号搭配使用:

    3.4)此时我们随便输入一个数据库中的用户名,密码是" or 1="1此时就会发生SQL注入:

    但是此时使用#的方式无论如何也不会发生SQL注入的问题: 

    我们在进行指定ID进行删除的时候还可以加上一个属性:

    表示要传递的参数的类型是啥

    1. <delete id="Delete" parameterType="java.lang.Integer">
    2. delete from user where userID=#{userID}
    3. delete>

    我们现在先实现一个场景,我们来进行查询一下UserID是7的学生信息(回顾)

    注意:我们再进行查询数据库的操作的时候,进行写方法的时候,不可以将返回值类型写成int,既然是查询,那么返回的一定是一条一条的纪录

     我们最终查询的url是:http://127.0.0.1:8080/SelectByID?userID=11

    一:UserController里面的代码:

    1. @Controller
    2. public class UserController {
    3. @Autowired
    4. UserService userService;
    5. @RequestMapping("/SelectByID")
    6. @ResponseBody
    7. public User SelectByID(Integer userID)
    8. {
    9. if(userID==null||userID.equals("")||userID<0)
    10. {
    11. return null;
    12. }
    13. return userService.SelectByID(userID);
    14. }
    15. }

    二:UserService里面的代码:

    1. @Service
    2. public class UserService {
    3. @Autowired
    4. private SpringBootMapper mapper;
    5. public User SelectByID(Integer userID) {
    6. return mapper.SelectByID(userID);
    7. }
    8. }

    XML文件里面的代码:

    1. "1.0" encoding="UTF-8"?>
    2. mapper PUBLIC "-//mybatis.org//DTD com.example.demo.Mapper1 3.0//EN"
    3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    4. <mapper namespace="com.example.demo.UserMapper.SpringBootMapper">
    5. <select id="SelectByID" resultType="com.example.demo.User">
    6. select * from user where userID=${userID}
    7. select>
    8. mapper>

    MyBatisMapper里面的代码:

      User SelectByID(Integer userID);
    1. #数据库连接配置:
    2. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springboot?characterEncoding=utf-8&useSSL=false
    3. spring.datasource.username=root
    4. spring.datasource.password=12503487
    5. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    6. mybatis.mapper-locations=classpath:mapper/**Mapper.xml
    7. #classpath表示项目的根路径,这里面的mapper和resources下的mapper是对应的,况且这里面的mapper的名称可以是任意的,我们想要在mapper目录里面创建
    8. #xml文件,后缀名就必须是Mapper.xml
    9. #开启MyBatisSQL语句打印
    10. mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

    上面进行的查询语句还可以写成这种,那么使用#{}和使用${}他们之间有什么区别那?

      select * from user where userID=#{userID}

    1)#是不会发生SQL注入的问题的,是当我们使用$时会发生SQL注入的问题,但是当我们使用order by进行查询的时候,我们所传递的参数一定要是程序员自己进行传入的,或者说当用户进行传入的时候我们直接调用replace方法直接把我们的单引号去掉,这样就可以减少SQL注入的发生

    2)下面是我们自己写的一个登录功能:

    1)UserController里面的代码:

    1. @Controller
    2. public class UserController {
    3. @Autowired
    4. UserService userService;
    5. @RequestMapping("/Login")
    6. @ResponseBody
    7. public HashMap login(String username, String password,HttpServletRequest req)
    8. {
    9. HashMap map=new HashMap<>();
    10. String message="登陆成功";
    11. int state=1;
    12. if(username==null||password==null||username.equals("")||password.equals(""))
    13. {
    14. String str="当前您传递的用户名或者密码不存在,说明您传递参数丢失";
    15. state=-1;
    16. }else {
    17. User user = userService.login(username,password);
    18. if (user == null || user.equals("") || user.getUserID() < 0) {
    19. message = "您当前登录失败,用户名错误,在数据库中无法查询";
    20. state = -1;
    21. } else {
    22. message = "您当前登录成功";
    23. HttpSession httpSession=req.getSession(true);
    24. httpSession.setAttribute("user",user);
    25. }
    26. }
    27. map.put("message",message);
    28. map.put("state",state);
    29. return map;
    30. }
    31. }

    2)UserService里面的代码:

    1. @Service
    2. public class UserService {
    3. @Autowired
    4. private SpringBootMapper mapper;
    5. public User login(String username,String password) {
    6. return mapper.login(username,password);
    7. }
    8. }

    3)我们写一下Mapper里面的代码:

    1. User login(String username,String password);
    2. xml文件里面的代码:
    3. "1.0" encoding="UTF-8"?>
    4. mapper PUBLIC "-//mybatis.org//DTD com.example.demo.Mapper1 3.0//EN"
    5. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    6. <mapper namespace="com.example.demo.UserMapper.SpringBootMapper">
    7. <select id="login" resultType="com.example.demo.User">
    8. select * from user where username=#{username} and password=#{password}
    9. select>
    10. mapper>

    但是当我们使用#的时候,查看程序是不会发生SQL注入的问题的:

    但是此时我们如果是把#改成$符,那么此时如果我们想输入密码还是' 1 or 1='1的时候,虽然不会登录成功,但是会成功的查询出所有的SQL语句:

    http://127.0.0.1:8080/Login?username=李佳伟&passwor=’ or 1='1

     select * from user where username="${username}" and password='${password}'

    所以我们一定要对前端用户输入的值进行过滤

    User user = userService.login(username,password.replace("'",""));

    1)#{}是预编译处理,是占位符,${}是字符串替换,是拼接符

    2)Mybatis在处理#{}的时候会将sql中的#{}替换成?号,调用PreparedStatement来赋值

    3)使用#{}是可以进行有效防止SQL注入的问题的

    4)我们来假设一个场景:他可以适用于SQL关键字的替换,我们需要在浏览器上面输入一个指令,来返回MYSQL进行查询的结果,根据querystring中的字段order返回结果,它只能有两个值,一个是asc一个是desc,我们可以根据userID来进行升序查询也可以进行降序查询-----下面我们来进行写一段代码进行演示:

    输入:127.0.0.1:8080/Java100?order=desc

    一:我们在UserController里面的代码:

    1. @Controller
    2. public class UserController {
    3. @Autowired
    4. UserService userService;
    5. @RequestMapping("/GetAll")
    6. @ResponseBody
    7. public List GetAll(String order)
    8. {
    9. //我们首先要在Controller层进行参数校验
    10. if(order==null||order.equals(""))
    11. {
    12. return null;
    13. }
    14. return userService.GetAll(order);
    15. }
    16. }

    二:我们在UserService里面的代码:

    1. @Service
    2. public class UserService {
    3. @Autowired
    4. private SpringBootMapper mapper;
    5. public List GetAll(String order) {
    6. return mapper.GetAll(order);
    7. }
    8. }

    三:我们在接口里面和XML文件里面的代码:

    1. List<User> GetAll(String order);
    2. "1.0" encoding="UTF-8"?>
    3. mapper PUBLIC "-//mybatis.org//DTD com.example.demo.Mapper1 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.example.demo.UserMapper.SpringBootMapper">
    6. <select id="GetAll" resultType="com.example.demo.User">
    7. select * from user order by userID ${order}
    8. select>
    9. mapper>

    我们如果在这里面直接使用的是#{变量名}的方式,程序就会发生报错,这个时候的查询语句就变成:

    select * from user order by userID ' 'desc' '

     但是这种情况是不会发生SQL注入的,是由程序员是进行这个操作的,不是由用户操作的,直接把单引号过滤掉,运用replace方法,里面加入要替换的字符串,过滤用户前台的输入词

    小技巧:我们在application.properties里面写上一句:就可以看到执行的SQL

    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

    SQL注入:'+空格+or+空格+or+1=’1;(and优先级最高)+后面还可以加上delete语句,把数据库搞没了

    总结:

    1)#是进行预处理,而$是直接进行替换

    2)#适用于所有的参数类型匹配,但是$只能适用于数值类型匹配

    2.1)也就是说#和$再进行数值替换的时候没有任何区别,得到的SQL语句都是相同的,对应的查询SQL语句都是正确的,现在从浏览器上面输入:127.0.0.1:8080/Java100?userID=1;

    #:select * from user where userID=1,同时$也是一样的(当我们进行数值匹配的时候,用#和$的效果是一样的,但是当我们进行传递的参数都是字符串类型的时候,就会出现问题了

    2.2)127.0.0.1:8080/Java100?username=A&password=123456

    但是针对于前端输入的字符串,#会自动将字符串加上双引号,但是$不会自动加上双引号

    #:select * from user where username="A"&password="123456"

    $:select * from user where username=A&password=123456,除非你给$加上

    select * from user where username="${username}" and password="${password}"

    因为如果说传递的参数是字符类型的,在SQL语法当中,如果是是字符类型,那么需要给值添加双引号,而$是自动进行替换,不会添加单双引号,所以程序就会直接报错了,但是#是使用占位符的方式直接进行使用的,所以不会存在任何问题;

    3)使用#不会发生SQL注入,使用$是会发生SQL注入问题的:

    3.1)User user= userMapper.SelectUser("A","' or 1='1");或者在浏览器上面输入

    127.0.0.1:8080/Java100?username=A&password=' or 1=1'

    使用$select * from user where username='${username}' and password='${password}'

    会发生SQL注入: select * from user where username='A' and password='' or 1='1',但是此时使用#不会发生SQL注入问题

    4)但是当我们进行传递的参数是一个查询关键字的时候,我们就要使用$的方式,例如进行SQL排序:127.0.0.1:8080/Java100?order=desc(使用SQL命令或者是SQL关键字)

    4.1)使用#:select * from user order by time #{order}

    会被自动替换成:select * from user order by time "desc"

    4.2)但是现在使用$符的方式:select * from user order by time ${order}

    会被自动替换成:select * from user order by time desc;

    5)当我们进行like模糊匹配的时候,我们优先使用$,但会发生SQL注入的问题,所以我们需要使用#的形式,还需要进行搭配MYSQL提供的内置函数

    select * from user where username like "%#{username}"使用这种格式是错误的,当我们输入:127.0.0.1:8080/Java100?username=a,最终就会变成"%"a"%"

    下面我们来演示一下like查询----根据用户在浏览器上面的输入不同的like的值来进行模糊匹配

    我们可以写一段代码:

    这是UserController里面的代码:

    1. @Controller
    2. public class UserController {
    3. @Autowired
    4. UserService userService;
    5. @RequestMapping("/SelectAll")
    6. @ResponseBody
    7. public List start(String name)
    8. {
    9. //我们还是需要在Controller层进行参数校验
    10. if(name==null||name.equals(""))
    11. {
    12. return null;
    13. }
    14. return userService.start(name);
    15. }
    16. }

    二:我们使用的UserService层的代码和接口层的代码:

    1. @Service
    2. public class UserService {
    3. @Autowired
    4. private SpringBootMapper mapper;
    5. public List start(String name) {
    6. return mapper.start(name);
    7. }
    8. }
    9. 接口层的代码:
    10. List start(String name);

    三:我们写的XML文件里面的代码:

    1. "1.0" encoding="UTF-8"?>
    2. mapper PUBLIC "-//mybatis.org//DTD com.example.demo.Mapper1 3.0//EN"
    3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    4. <mapper namespace="com.example.demo.UserMapper.SpringBootMapper">
    5. <select id="start" resultType="com.example.demo.User">
    6. select * from user where username like '%${name}%'
    7. select>
    8. mapper>

    1)我们如果使用$是可以的,但是很有可能会出现安全问题,可以直接过滤,但是如果name有可能是中文名,也有可能是英文名时可能会出现’字符的,所以我们不建议使用这种方法

    2)但是我们不可以直接使用#的方式来进行模糊匹配,但是可以使用所以我们可以进行使用MYSQL提供的内置函数,这样做既可以保证了安全,还可以使用#的方式

      select * from user where username like concat('%',#{name},'%')
  • 相关阅读:
    Python从零到就业
    QT5自定义下拉框为QTreeView类型(树形分上下级)的下拉框(QComboBox)(超详细步骤)
    ESP32设备通信-LoRaWAN网关
    编译原理笔记01
    Flutter实践二:repository模式
    R语言经典统计分析
    TS以及webpack的es module
    2023NOIP A层联测18 划分
    计算机图形学6--讨论多边形
    Python 给视频添加水印
  • 原文地址:https://blog.csdn.net/weixin_61518137/article/details/127786747