Mybatis:SQL注入问题 like模糊查询 多表查询 动态SQL

简介: Mybatis:SQL注入问题 like模糊查询 多表查询 动态SQL

一:#{}和${}的区别

  在上一篇博客中,我们在博客的末尾简单的总结了#{},${},这两个注解的区别。那么,这篇博客我们来详细的了解两者的区别。

#{}:将其内容加上单引号。

${}:直接进行替换。常用于替换SQL中的关键字,eg:desc/asc。

1.1:实例

我们按照userinfo的id将用户的信息按降序排序。

1.1.1:Mapper中声明的方法:

List<UserInfo> getlistByOrder(@Param("order") String order);

1.1.2:XML中的配置

<select id="getlistByOrder" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo order by id ${order}
    </select>

1.1.3:生成测试方法+运行结果

@Test
    void getlistByOrder() {
        String order="desc";
        List<UserInfo> userInfos = userMapper.getlistByOrder(order);
        for (UserInfo userInfo : userInfos) {
            System.out.println(userInfo);
        }
    }

如上图所示,我们会发现当把order这个字符串传进去的时候,由${}进行处理,此时,直接进行了替换,并没有进行加''。这是最本质的区别。

二:SQL注入问题

      从上文我们可以了解到,${}和#{}最本质的区别是一个加了单引号,一个直接进行替换。那么,如果我们将#{}替换成${}会发生什么?下面,我们将利用登录这一假设的实例:

2.1:实例

2.1.1Mapper中声明的方法

//输入用户的账户和密码
    UserInfo login(@Param("username") String username,@Param("password")String password);

2.1.2:XML中的配置

<select id="login" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo where username=${username} and password=${password}
    </select>

2.2.3:生成测试方法+运行结果

@Test
    void login() {
        String username="李明轩";
        String password="123456";
        UserInfo login = userMapper.login(username, password);
        System.out.println(login==null?"失败":"成功");
    }

从测试用例不难看出,SQL语句出问题了,可见,${}是直接进行替换的。那么,我们在${}加上单引号呢?

<select id="login" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo where username='${username}' and password='${password}'
    </select>

运行成功:从这一实例不难看出,${}是直接替换的,加上单引号之后,就和#{}拥有一样的功能了。

2.2:由${}引出的SQL注入问题

2.2.1测试的代码

@Test
    void login() {
        String username="李明轩";
        String password="'or 1= '1 ";
        UserInfo login = userMapper.login(username, password);
        System.out.println(login==null?"失败":"成功");
    }

2.2.2: XML中的配置

<select id="login" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo where username='${username}' and password='${password}'
    </select>

2.2.3:运行结果:

通过上述测试,我们发现当用户存在时,密码不正确时,同样也可以将其用户信息打印出来,为什么呢?

2.3:什么时候用${}

三:like模糊查询

在前面我们学习SQL语句中,有模糊查询,为like。我们在mybatis来使用一下like查询。

3.1实例

3.1.1:Mapper中声明的方法

//查询用户名字中含有李的用户信息
    List<UserInfo> getListByName(String username);

3.1.2:XML中的配置

<select id="getListByName" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo where username like '%#{username}%'
    </select>

3.1.3:生成测试方法+运行结果

@Test
    void getListByName() {
        String username="李";
        List<UserInfo> userInfos = userMapper.getListByName(username);
        System.out.println(userInfos);
    }

我们会发现,当这样运行的时候,SQL语句会爆出错误,为什么呢?

3.2如何进行解决

利用mysql中提供的concat进行拼接。我们来进行验证。

<select id="getListByName" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo where username like concat('%',#{username},'%')
    </select>

运行结果:

可见,当我们使用SQL语句中的普通的like模糊查询时,要注意利用concat进行拼接。

四:多表查询

4.1:当数据库里面的字段名和对象的属性名不一致时

看如下两张图:我们会发现数据库中的字段名的username和对象的 name不一致,我们该如何解决?

数据库中的字段名:

对象的属性名:

@Data
public class UserInfo {
     private int id;
    private String name;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private int state;
}

4.1.1解决方案一:

首先在*Mapper.xml声明对ResultMap的配置:

<resultMap id="baseMap" type="com.example.demo10.entity.UserInfo">
        <id column="id" property="id"></id>
        <result column="usernmae" property="name"></result>
        <result column="password" property="password"></result>
        <result column="photo" property="photo"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="state" property="state"></result>
    </resultMap>

<select id="getAll1" resultMap="baseMap">
        select * from userinfo;
    </select>

Mapper中的声明方法:

 

//查询所有的用户
    List<UserInfo> getAll1();

生成测试方法+运行结果:

@Test
    void getAll1() {
        List<UserInfo> all1 = userMapper.getAll1();
        for (UserInfo userInfo : all1) {
            System.out.println(userInfo);
        }
    }

4.1.2解决方案二:(常用的方案

<select id="getAll1" resultType="com.example.demo10.entity.UserInfo" >
        select id,username as name ,password,photo,createtime,updatetime,state from userinfo
    </select>

生成测试方法+运行结果:

@Test
    void getAll1() {
        List<UserInfo> all1 = userMapper.getAll1();
        for (UserInfo userInfo : all1) {
            System.out.println(userInfo);
        }
    }

运行结果:

4.2:一对一多表查询

目标要求;我们设计了两张表,用户表和文章表,那么,我们需要利用用户表的id对应文章表的uid,并查询出文章表的所有信息和用户名称

用户表:

文章表:

在entity的目录下创建视图类和文章类。

文章类

文章视图类

将所需要两张表查询的所有信息创建一个试图类,依据要求,此类需要包括用户的姓名和文章表的所有信息。

Mapper中声明的方法:

@Mapper
public interface ArticleMapper {
    ArticleinfoView getAll(Integer id);
}

XML中的配置:

<mapper namespace="com.example.demo10.mapper.ArticleMapper">
    <select id="getAll" resultType="com.example.demo10.entity.ArticleinfoView">
        select a.*,u.username from articleinfo a left join userinfo  u  on u.id=a.uid
         where a.id=#{u.id}
    </select>
</mapper>

生成测试方法+运行结果:

@SpringBootTest
@Transactional
class ArticleMapperTest {
  @Autowired
  private ArticleMapper articleMapper;
    @Test
    void getAll() {
        ArticleinfoView all = articleMapper.getAll(1);
        System.out.println(all);
    }
}

运行结果:

4.3:一对多查询

实现目标:查询一个用户有多少个文章时,我们可以用一条SQL语句进行查询。

<select id="getAll1" resultType="com.example.demo10.entity.ArticleinfoView">
        select * from articleinfo where uid=#{id};
    </select>

4.4:多对多查询

针对多对多查询,可以看成是两个一对多查询。

五:动态SQL

当我们使用普通的SQL解决不了问题时,可以采用动态SQL解决问题。有如下五个动态SQL。

5.1:<if>标签

5.1.1:使用案例

Mapper中声明的方法:

//添加用户信息
    int add2(UserInfo userInfo);

XML中的配置:

<insert id="add2">
        insert into userinfo(
                             username,
                             <if test="photo!=null">
                                 photo,
                             </if>
                             password
        )values (#{username},
                <if test="photo!=null">
                    #{photo},
                </if>
             password=#{password})
    </insert>

测试方法+运行结果

void add2() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("李四");
        userInfo.setPassword("12345");
        userInfo.setPhoto(null);
        int i = userMapper.add2(userInfo);
        System.out.println(i);
    }

5.2:<trim>标签

5.2.1使用案例

Mapper中声明的方法:

//添加用户信息
    int add3(UserInfo userInfo);

XML中的配置:

<insert id="add3">
        insert into userinfo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null">
                username,
            </if>
            <if test="password!=null">
                password,
            </if>
            <if test="photo=null">
                photo
            </if>
        </trim>
            values
                <trim prefix="(" suffix=")" suffixOverrides="," >
                    <if test="username!=null">
                        #{username},
                    </if>
                    <if test="password!=null">
                        #{password},
                    </if>
                    <if test="photo=null">
                        #{photo},
                    </if>
                </trim>
    </insert>

测试方法+运行结果:

@Test
    void add3() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("老六");
        userInfo.setPassword("123456");
        userInfo.setPhoto(null);
        int i = userMapper.add2(userInfo);
        System.out.println(i);
    }

5.3:<set>标签

5.4:<where>标签

<where><if> 进行组合.

如果我们将and加在第一个<if>标签下,当第二个<if>标签不成立,此时,会对整个sql产生影响,整个SQL

select * from userinfo where username=#{username} and

上面这个语句便会报错,不符合整个SQL的语句规范。

当条件不成立时,if条件后的内容包括and也不会存在,因此会对整个sql语句产生影响。

注意and关键字要放在每个<if>语句中的库表字段赋值的前面。因为,一旦判断不成功,<where> 会把对应的and关键字去掉(还有or关键字)。

5.4.1使用案例:

Mapper中的声明的方法:

//查询用户的信息,当传入的用户,密码其中一条为空时,能不能查询到信息
    UserInfo getall(String username,String password);

XML中的配置:

<select id="getall" resultType="com.example.demo10.entity.UserInfo">
        select * from userinfo
        <where>
            <if test="username!=null">
                username=#{username}
            </if>
            <if test="password!=null">
               and password=#{password}
            </if>
        </where>
    </select>

测试方法+运行结果:

@Test
    void getall() {
        String usernmae="李明轩";
        String password=null;
        UserInfo getall = userMapper.getall(usernmae, password);
        System.out.println(getall);
    }

5.5:<foreach>标签

当我们想要根据多个id删除多个数据时,我们使用的SQL语句如下:

delete from userinfo where id in(id1,id2,id23)

那么,我们借助<foreach>可以完成操作。

5.5.1使用案例:

Mapper中声明的方法:

//根据多条id删除数据
    int dels(List<Integer> ids);

XML中的配置:

<delete id="dels">
        delete from userinfo
        where id in
            <foreach collection="ids" item="id" open="(" close=")" separator=",">
                #{id}
            </foreach>
    </delete>

测试方法+运行结果:

@Test
    void dels() {
        List<Integer> list=new ArrayList<>();
        list.add(6);
        list.add(7);
        int dels = userMapper.dels(list);
        System.out.println(dels);
    }

相关文章
|
12天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
1月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
30 10
|
2月前
|
SQL XML Java
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
文章介绍了MyBatis中动态SQL的用法,包括if、choose、where、set和trim标签,以及foreach标签的详细使用。通过实际代码示例,展示了如何根据条件动态构建查询、更新和批量插入操作的SQL语句。
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
|
2月前
|
SQL 安全 数据库
惊!Python Web安全黑洞大曝光:SQL注入、XSS、CSRF,你中招了吗?
在数字化时代,Web应用的安全性至关重要。许多Python开发者在追求功能时,常忽视SQL注入、XSS和CSRF等安全威胁。本文将深入剖析这些风险并提供最佳实践:使用参数化查询预防SQL注入;通过HTML转义阻止XSS攻击;在表单中加入CSRF令牌增强安全性。遵循这些方法,可有效提升Web应用的安全防护水平,保护用户数据与隐私。安全需持续关注与改进,每个细节都至关重要。
128 5
|
2月前
|
SQL 安全 数据库
深度揭秘:Python Web安全攻防战,SQL注入、XSS、CSRF一网打尽!
在Web开发领域,Python虽强大灵活,却也面临着SQL注入、XSS与CSRF等安全威胁。本文将剖析这些常见攻击手段,并提供示例代码,展示如何利用参数化查询、HTML转义及CSRF令牌等技术构建坚固防线,确保Python Web应用的安全性。安全之路永无止境,唯有不断改进方能应对挑战。
65 5
|
2月前
|
SQL 安全 数据安全/隐私保护
Python Web安全大挑战:面对SQL注入、XSS、CSRF,你准备好了吗?
在构建Python Web应用时,安全性至关重要。本文通过三个真实案例,探讨了如何防范SQL注入、XSS和CSRF攻击。首先,通过参数化查询替代字符串拼接,防止SQL注入;其次,利用HTML转义机制,避免XSS攻击;最后,采用CSRF令牌验证,保护用户免受CSRF攻击。这些策略能显著增强应用的安全性,帮助开发者应对复杂的网络威胁。安全是一个持续的过程,需不断学习新知识以抵御不断变化的威胁。
115 1
|
2月前
|
SQL 安全 数据库
Python Web开发者必看!SQL注入、XSS、CSRF全面解析,守护你的网站安全!
在Python Web开发中,构建安全应用至关重要。本文通过问答形式,详细解析了三种常见Web安全威胁——SQL注入、XSS和CSRF,并提供了实用的防御策略及示例代码。针对SQL注入,建议使用参数化查询;对于XSS,需对输出进行HTML编码;而防范CSRF,则应利用CSRF令牌。通过这些措施,帮助开发者有效提升应用安全性,确保网站稳定运行。
47 1
|
2月前
|
SQL 安全 数据库
深度揭秘:Python Web安全攻防战,SQL注入、XSS、CSRF一网打尽!
在Web开发领域,Python虽强大灵活,但安全挑战不容小觑。本文剖析Python Web应用中的三大安全威胁:SQL注入、XSS及CSRF,并提供防御策略。通过示例代码展示如何利用参数化查询、HTML转义与CSRF令牌构建安全防线,助您打造更安全的应用。安全是一场持久战,需不断改进优化。
46 3
|
2月前
|
SQL 安全 数据库
从入门到精通:Python Web安全守护指南,SQL注入、XSS、CSRF全防御!
【9月更文挑战第13天】在开发Python Web应用时,安全性至关重要。本文通过问答形式,详细介绍如何防范SQL注入、XSS及CSRF等常见威胁。通过使用参数化查询、HTML转义和CSRF令牌等技术,确保应用安全。附带示例代码,帮助读者从入门到精通Python Web安全。
86 6
|
2月前
|
SQL 安全 JavaScript
告别Web安全小白!Python实战指南:抵御SQL注入、XSS、CSRF的秘密武器!
【9月更文挑战第12天】在Web开发中,安全漏洞如同暗礁,尤其对初学者而言,SQL注入、跨站脚本(XSS)和跨站请求伪造(CSRF)是常见挑战。本文通过实战案例,展示如何利用Python应对这些威胁。首先,通过参数化查询防止SQL注入;其次,借助Jinja2模板引擎自动转义机制抵御XSS攻击;最后,使用Flask-WTF库生成和验证CSRF令牌,确保转账功能安全。掌握这些技巧,助你构建更安全的Web应用。
50 5