mybatis中数据加密与解密的实现

目录
  • 1、需求
  • 2、解决方案
  • 3、使用拦截器方式
    • 3.1定义加密接口
    • 3.2定义加密注解
    • 3.3拦截器加密数据
    • 3.4拦截器解密数据
    • 3.5解密工具类
    • 3.6实体类样例
  • 4、使用类型转换器
    • 4.1定义加密类型
    • 4.2定义类型转换处理器
    • 4.3配置类型转换器的包路径
    • 4.4测试用的实体类
    • 4.5mapper接口文件
    • 4.6mapper映射文件

数据加解密的实现方式多种多样,在mybatis环境中数据加解密变得非常简单易用,本文旨在提供参考,在生产中应尽可能完成单元测试,开展足够的覆盖测试,以验证可靠性、可用性、安全性。

1、需求

**原始需求:**数据在保存时进行加密,取出时解密,避免被拖库时泄露敏感信息。

**初始分析:**数据从前端过来,到达后端,经过业务逻辑后存入数据库,其中经历三大环节:

1、前端与后端之间传输,是否加密,如果需要加密则前端传输前就需要加密,暂时可以用HTTPS代替;
2、到达后端,此时数据通常需要经过一些逻辑判断,所以加密没有意义,反而会带来不必要的麻烦;
3、入库,这个是最后环节,数据经过insert的sql或者update语句入库,在此前需要加密;

**核心需求:**入库前最后一步完成数据加密,达成的目的是如果数据库被暴露,一定程度上保障数据的安全,也可以防止有数据操作权限的人将数据泄露。

**加密算法:**对称和非对称算法均可,考虑加密和解密的效率以及场景,考虑选用对称算法AES加密。

**ORM环境:**mybatis

**加密字段:**加密字段不确定,应该在数据库表设计的时候确定敏感字段,即加密字段可定制。

应注意的细节:

1、某个字段被加密后,其字段的存取性能下降,加密字段越多性能下降就越多,无具体指标;
2、字段被加密后,该字段的索引没有太大意义,比如对手机号码字段mobile加密,原先可能设计为唯一索引以防止号码重复,加密后密文性能下降,比对结果不直观,没有大量数据验证,理论上密文也不会相同;
3、一些SQL的比对也无法直接实现,比如手机号码匹配查询,在开发和运维中,就需要考虑后续工作中敏感字段的可操作性;
4、原字段的长度需要扩充,密文肯定比原文长;
5、不要对主键加密(真的,有人会这么做的);
6、有时,为了减少关联查询,我们会对表做冗余字段,比如将name字段放入业务表,如果对name字段加密,则需同步对冗余表做加密处理,所以在进行数据加密需求时,应进行全局考虑。

最后:数据加密用来提高安全性的同时,必然会牺牲整个程序性能和易用性。

2、解决方案

在mybatis的依赖环境下,至少有两种自动加密的方式:

1、使用拦截器,对insert和update语句拦截,获取需加密字段,加密后存入数据库。读取时拦截query,解密后存入result对象;      
2、使用类型转换器TypeHandler来实现。

3、使用拦截器方式

3.1 定义加密接口

因为mybatis拦截器会拦截所有符合签名的请求,为了提高效率定义一个标记接口非常重要,既然有接口不如就在接口里加入需要加密的字段信息,当然也可以不加,根据实际场景来设计。

/**
 * @author: xu.dm
 * @since: 2022/3/8 16:30
 * 该接口用于标记实体类需要加密,具体的加密内容字段通过getEncryptFields返回.
 * 注意:getEncryptFields与@Encrypt注解可配合使用也可以互斥使用,根据具体的需求实现。
 **/
public interface Encrypted {
    /**
     * 实现该接口,返回需要加密的字段名数组,需与类中字段完全一致,区分大小写
     * @return 返回需要加密的字段
     */
    default String[] getEncryptFields() {
        return new String[0];
    }
}

3.2 定义加密注解

主要为了某些场景,直接在实体类的字段打标记,直观的说明该字段是加密字段,某些业务逻辑也可以依赖此标记做进一步操作,一句话,根据场景来适配和设计。

/**
 * @author : xu.dm
 * @since : 2022/3/8
 * 标识加密的注解,value值暂时没用,根据需要可以考虑采用的加密方式与算法等
 * 注意:Encrypted接口的getEncryptFields与@Encrypt注解可配合使用也可以互斥使用,根据具体的需求实现。
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
    String value() default "";
}

3.3 拦截器加密数据

初始拦截器定义是相对单一的场景,利用反射遍历需加密的字段,对字段的字符加密,也就是待加密字段最好是字符串类型,并且,没有对父类反射遍历,如果有继承情况,并且父类也有需要加密的字段,需根据场景调整代码,对父类递归,直到根父类。在当前设计中Encrypted接口和@Encrypt只会生效一种,并且以接口优先。

/**
 * @author: xu.dm
 * @since: 2022/3/8
 * 拦截所有实现Encrypted接口的实体类insert和update操作
 * 如果接口的getEncryptFields返回数组长度大于0,则使用该参数进行加密,
 * 否则检查实体类中带@Encrypt注解,对该标识字段加密,
 * 注意:待加密的字段最好是字符串,加密调用的是标识对象的ToString()结果进行加密,
 *
 **/
@Component
@Slf4j
@Intercepts({
        @Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class})
})
public class EncryptionInterceptor implements Interceptor {

    public EncryptionInterceptor() {

    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object[] args = invocation.getArgs();
        SqlCommandType sqlCommandType = null;

        for (Object object : args) {
            // 从MappedStatement参数中获取到操作类型
            if (object instanceof MappedStatement) {
                MappedStatement ms = (MappedStatement) object;
                sqlCommandType = ms.getSqlCommandType();
                log.debug("Encryption interceptor 操作类型: {}", sqlCommandType);
                continue;
            }
            log.debug("Encryption interceptor 操作参数:{}",object);

            // 判断参数
            if (object instanceof Encrypted) {
                if (SqlCommandType.INSERT == sqlCommandType) {
                    encryptField((Encrypted)object);
                    continue;
                }
                if (SqlCommandType.UPDATE == sqlCommandType) {
                    encryptField((Encrypted)object);
                    log.debug("Encryption interceptor update operation,encrypt field: {}",object.toString());
                }
            }
        }
        return invocation.proceed();
    }

    /**
     * @param object 待检查的对象
     * @throws IllegalAccessException
     * 通过查询注解@Encrypt或者Encrypted返回的字段,进行动态加密
     * 两种方式互斥
     */
    private void encryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        String factor = "xu.dm118dAADF!@$";
        Class<?> clazz = object.getClass();

        if(encryptFields.length==0){
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if(encrypt!=null) {
                    String encryptString = AesUtils.encrypt(field.get(object).toString(), factor);
                    field.set(object,encryptString);
                    log.debug("Encryption interceptor,encrypt field: {}",field.getName());
                }
            }
        }else {
            for (String fieldName : encryptFields) {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                String encryptString = AesUtils.encrypt(field.get(object).toString(), factor);
                field.set(object,encryptString);
                log.debug("Encryption interceptor,encrypt field: {}",field.getName());
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

}

3.4 拦截器解密数据

解密时拦截query方法,只对结果集判断,结果属于Encrypted接口或者结果结果集第一条数据属于Encrypted接口则进入解密流程。

解密失败或者解密方法返回空串后,不会修改原本字段数据。

/**
 * @author: xu.dm
 * @since: 2022/3/9 11:39
 * 解密数据,返回结果为list集合时,应保证集合里都是同一类型的元素。
 * 解密失败时返回为null,或者返回为空串时,不对原数据操作。
 **/
@Component
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class DecryptionInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if(result instanceof ArrayList) {
            @SuppressWarnings("rawtypes")
            ArrayList list = (ArrayList) result;
            if(list.size() == 0) {
                return result;
            }
            if(list.get(0) instanceof Encrypted) {
                for (Object item : list) {
                    decryptField((Encrypted) item);
                }
            }
            return result;
        }
        if(result instanceof Encrypted) {
            decryptField((Encrypted) result);
        }
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * @param object 待检查的对象
     * @throws IllegalAccessException
     * 通过查询注解@Encrypt或者Encrypted返回的字段,进行解密
     * 两种方式互斥
     */
    private void decryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        String factor = "xu.dm118dAADF!@$";
        Class<?> clazz = object.getClass();

        if(encryptFields.length==0){
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if(encrypt!=null) {
                    String encryptString = AesUtils.decrypt(field.get(object).toString(), factor);
                    if(encryptString!=null){
                        field.set(object,encryptString);
                        log.debug("Encryption interceptor,encrypt field: {}",field.getName());
                    }
                }
            }
        }else {
            for (String fieldName : encryptFields) {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                String encryptString = AesUtils.decrypt(field.get(object).toString(), factor);
                if(encryptString!=null && encryptString.length() > 0){
                    field.set(object,encryptString);
                    log.debug("Encryption interceptor,encrypt field: {}",field.getName());
                }
            }
        }
    }
}

3.5 解密工具类

解密工具类可根据场景进一步优化,例如:可考虑解密类实例化后常驻内存,以减少CPU负载。

/**
 * @author: xu.dm
 * @since: 2018/11/24 22:26
 *
 */
public class AesUtils {
    private static final String ALGORITHM = "AES/ECB/PKCS5Padding";

    public static String encrypt(String content, String key) {
        try {
            //获得密码的字节数组
            byte[] raw = key.getBytes();
            //根据密码生成AES密钥
            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
            //根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            //初始化密码器,第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二个参数为生成的AES密钥
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            //获取加密内容的字节数组(设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte [] contentBytes = content.getBytes(StandardCharsets.UTF_8);
            //密码器加密数据
            byte [] encodeContent = cipher.doFinal(contentBytes);
            //将加密后的数据转换为字符串返回
            return Base64.encodeBase64String(encodeContent);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("AesUtils加密失败");
        }
    }

    public static String decrypt(String encryptStr, String decryptKey) {
        try {
            //获得密码的字节数组
            byte[] raw = decryptKey.getBytes();
            //根据密码生成AES密钥
            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
            //根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            //初始化密码器,第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二个参数为生成的AES密钥
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            //把密文字符串转回密文字节数组
            byte [] encodeContent = Base64.decodeBase64(encryptStr);
            //密码器解密数据
            byte [] byteContent = cipher.doFinal(encodeContent);
            //将解密后的数据转换为字符串返回
            return new String(byteContent, StandardCharsets.UTF_8);
        } catch (Exception e) {
            // e.printStackTrace();
            // 解密失败暂时返回null,可以抛出runtime异常
            return null;
        }
    }
}

3.6 实体类样例

/**
 * (SysUser)实体类
 *
 * @author xu.dm
 * @since 2020-05-02 09:34:53
 */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(exclude = {"password","username"},callSuper = true)
public class SysUser extends BaseDO implements Serializable, Encrypted {
    private static final long serialVersionUID = 100317866935565576L;
    /**
    * ID 转换成字符串给前端,否则js会出现精度问题
    * 对于前后台传参Long类型64位而言,当前端超过53位后会丢失精度,超过的部分会以00的形式展示.
     * 可以使用   @JsonSerialize(using = ToStringSerializer.class)
    */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /**
    * 手机号码
    */
    @Encrypt
    private String mobile;
    /**
    * 用户登录名称
    */
    private String username;

    private String name;
    /**
    * 密码
    */
    @JsonIgnore
    private String password;
    /**
    * email
    */
    private String email;

    @Override
    public String[] getEncryptFields() {
        return new String[]{"mobile","name"};
    }
}

4、使用类型转换器

在mybatis中使用类型转换器,本质上就是就自定义一个类型(本质就是一个类),通过mybatis提供的TypeHandler接口扩展,对数据类型转换,在这个过程中加入加密和解密业务逻辑实现数据存储和查询的加解密功能。

4.1 定义加密类型

这个类型就直接理解成类似java.lang.String。如果对加密的方式有多种需求,可扩N种EncryptType类型。

/**
 * @author: xu.dm
 * @since: 2022/3/9 16:54
 * 自定义类型,用于在mybatis中表示加密类型
 * 需要加密的字段使用EncryptType声明
 **/
public class EncryptType {
    private String value;

    public EncryptType() {
    }

    public EncryptType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return value;
    }
}

4.2 定义类型转换处理器

AesUtils工具类见上文描述。

转换器继承自mybatisBaseTypeHandler,重写值设置和值获取的方法,在其过程中加入加密和解密逻辑。

/**
 * @author: xu.dm
 * @since: 2022/3/9 16:21
 * 类型转换器,处理EncryptType类型,用于数据加解密
 **/
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(EncryptType.class)
public class EncryptTypeHandler extends BaseTypeHandler<EncryptType> {
    private String factor = "xu.dm118dAADF!@$";

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, EncryptType parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null || parameter.getValue() == null) {
            ps.setString(i, null);
            return;
        }
        String encrypt = AesUtils.encrypt(parameter.getValue(),factor);
        ps.setString(i, encrypt);
    }

    @Override
    public EncryptType getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String decrypt = AesUtils.decrypt(rs.getString(columnName), factor);
        if(decrypt==null || decrypt.length()==0){
            decrypt = rs.getString(columnName);
        }
        return new EncryptType(decrypt);
    }

    @Override
    public EncryptType getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String decrypt = AesUtils.decrypt(rs.getString(columnIndex), factor);
        if(decrypt==null || decrypt.length()==0){
            decrypt = rs.getString(columnIndex);
        }
        return new EncryptType(decrypt);
    }

    @Override
    public EncryptType getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String decrypt = AesUtils.decrypt(cs.getString(columnIndex), factor);
        if(decrypt==null || decrypt.length()==0){
            decrypt = cs.getString(columnIndex);
        }
        return new EncryptType(decrypt);
    }
}

4.3 配置类型转换器的包路径

这个配置是可选的,因为可以在mapper的映射xml文件中指定。

mybatis:
  #xml映射版才需要配置,纯注解版本不需要
  mapper-locations: classpath*:mapper/*.xml #多模块指定sql映射文件的位置,需要在classpath后面多加一个星号
  type-handlers-package: com.wood.encryption.handler

4.4 测试用的实体类

截取了部分代码,关注代码中使用EncryptType类型的字段name和mobile。

/**
 * (TestUser)实体类
 *
 * @author xu.dm
 * @since 2022-03-10 11:31:54
 */
@Data
public class TestUser extends BaseDO implements Serializable {
    private static final long serialVersionUID = -53491943096074862L;
    /**
     * ID
     */
    private Long id;
    /**
     * 手机号码
     */
    private EncryptType mobile;
    /**
     * 用户登录名称
     */
    private String username;
    /**
     * 用户名或昵称
     */
    private EncryptType name;
    /**
     * 密码
     */
    private String password;
    /**
     * email
     */
    private String email;

    ... ...

}

4.5 mapper接口文件

这个类没有本质的变化,截取了部分代码,注意EncryptType类型的使用。

/**
 * (TestUser)表数据库访问层
 *
 * @author xu.dm
 * @since 2022-03-10 11:31:54
 */
public interface TestUserDao {

    /**
     * 查询手机号码,通过主键
     *
     * @param id 主键
     * @return 手机号码
     */
    EncryptType queryMobileById(Long id);

    /**
     * 通过手机号码查询单条数据
     *
     * @param mobile 手机号码
     * @return 实例对象
     */
    List<TestUser> queryByMobile(EncryptType mobile);

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    TestUser queryById(Long id);

    /**
     * 查询所有数据,根据入参,决定是否模糊查询
     *
     * @param testUser 查询条件
     *
     * @return 对象列表
     */
    List<TestUser> queryByBlurry(TestUser testUser);

    /**
     * 统计总行数
     *
     * @param testUser 查询条件
     * @return 总行数
     */
    long count(TestUser testUser);

    /**
     * 新增数据
     *
     * @param testUser 实例对象
     * @return 影响行数
     */
    int insert(TestUser testUser);

    /**
     * 修改数据
     *
     * @param testUser 实例对象
     * @return 影响行数
     */
    int update(TestUser testUser);

}

4.6 mapper映射文件

没有本质变化,截取了部分代码,注意EncryptType类型的使用。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wood.system.dao.TestUserDao">

    <resultMap type="com.wood.system.entity.TestUser" id="TestUserMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="mobile" column="mobile" jdbcType="VARCHAR"/>
        <result property="username" column="username" jdbcType="VARCHAR"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="email" column="email" jdbcType="VARCHAR"/>
        <result property="state" column="state" jdbcType="VARCHAR"/>
        <result property="level" column="level" jdbcType="VARCHAR"/>
        <result property="companyId" column="company_id" jdbcType="INTEGER"/>
        <result property="deptId" column="dept_id" jdbcType="INTEGER"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
    </resultMap>

    <!--查询单个-->
    <select id="queryById" resultMap="TestUserMap">
        select
          id, mobile, username, name, password, email, state, level, company_id, dept_id, create_time, update_time
        from test_user
        where id = #{id}
    </select>

    <!--查询指定行数据-->
    <select id="queryByBlurry" resultMap="TestUserMap">
        select
          id, mobile, username, name, password, email, state, level, company_id, dept_id, create_time, update_time
        from test_user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="mobile != null and mobile != ''">
                and mobile = #{mobile}
            </if>
            <if test="username != null and username != ''">
                and username = #{username}
            </if>
            <if test="name != null and name != ''">
                and name = #{name}
            </if>
			... ...

        </where>
    </select>

    <select id="queryMobileById" resultType="com.wood.encryption.type.EncryptType">
        select mobile from test_user where id = #{id}
    </select>

    <select id="queryByMobile" resultType="com.wood.system.entity.TestUser">
        select * from test_user where mobile = #{mobile}
    </select>

    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="false">
        insert into test_user(id, mobile, username, name, password, email, state, level, company_id, dept_id, create_time, update_time)
        values (#{id}, #{mobile}, #{username}, #{name}, #{password}, #{email}, #{state}, #{level}, #{companyId}, #{deptId}, #{createTime}, #{updateTime})
    </insert>

    <!--通过主键修改数据-->
    <update id="update">
        update test_user
        <set>
            <if test="mobile != null and mobile != ''">
                mobile = #{mobile},
            </if>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="name != null and name != ''">
                name = #{name},
            </if>

            <if test="email != null and email != ''">
                email = #{email},
            </if>
            <if test="state != null and state != ''">
                state = #{state},
            </if>
            <if test="level != null and level != ''">
                level = #{level},
            </if>
            <if test="companyId != null">
                company_id = #{companyId},
            </if>
            <if test="deptId != null">
                dept_id = #{deptId},
            </if>

            <if test="updateTime != null">
                update_time = #{updateTime},
            </if>
        </set>
        where id = #{id}
    </update>

</mapper>

到此这篇关于mybatis中数据加密与解密的实现的文章就介绍到这了,更多相关mybatis数据加密与解密内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MyBatis-Plus如何实现自动加密解密

    目录 MyBatis-Plus 自动加密解密 实现TypeHandler 添加注解 查询加密字段 MyBatis-Plus 敏感数据的加密 写加密解密的工具类 继承BaseTypeHandler ,实现对数据的转换 有po类中,实现相关类型注解 MyBatis-Plus 自动加密解密 通过使用MyBatis的typeHandler功能,对入参和出参进行处理,实现无缝加密解密(将明文加密后保存至数据库:从数据库读取时,自动将密文解密成明文) 实现TypeHandler @Slf4j public

  • mybatis中数据加密与解密的实现

    目录 1.需求 2.解决方案 3.使用拦截器方式 3.1定义加密接口 3.2定义加密注解 3.3拦截器加密数据 3.4拦截器解密数据 3.5解密工具类 3.6实体类样例 4.使用类型转换器 4.1定义加密类型 4.2定义类型转换处理器 4.3配置类型转换器的包路径 4.4测试用的实体类 4.5mapper接口文件 4.6mapper映射文件 数据加解密的实现方式多种多样,在mybatis环境中数据加解密变得非常简单易用,本文旨在提供参考,在生产中应尽可能完成单元测试,开展足够的覆盖测试,以验证可

  • Yii 实现数据加密和解密的示例代码

    在Yii中又很多的数据我们不能够以明文进行存储和展示,例如密码和一些比较重要的文件信息,这时候我们都需要我们对这些信息进行加密 一:对密码进行加密和验证客户端传输的密码是否正确 1:对密码进行加密 $hash = Yii::$app->getSecurity()->generatePasswordHash($password); 2:对客户端传递的密码进行验证,判断是否正确 //$password:客户端传递的明文密码,$hash:对密码进行加密后的哈希值 if (Yii::$app->

  • MySQL数据库对敏感数据加密及解密的实现方式

    大数据时代的到来,数据成为企业最重要的资产之一,数据加密的也是保护数据资产的重要手段.本文主要在结合学习通过MySQL函数及Python加密方法来演示数据加密的一些简单方式. 1. 准备工作 为了便于后面对比,将各种方式的数据集存放在不同的表中. 创建原始明文数据表 /* 创建原始数据表 */ CREATE TABLE `f_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `tel` v

  • Mybatis的TypeHandler加解密数据实现

    一.背景 在我们数据库中有些时候会保存一些用户的敏感信息,比如: 手机号.银行卡等信息,如果这些信息以明文的方式保存,那么是不安全的.假如: 黑客黑进了数据库,或者离职人员导出了数据,那么就可能导致这些敏感数据的泄漏.因此我们就需要找到一种方法来解决这个问题. 二.解决方案 由于我们系统中使用了Mybatis作为数据库持久层,因此决定使用Mybatis的TypeHandler或Plugin来解决. TypeHandler : 需要我们在某些列上手动指定 typeHandler 来选择使用那个ty

  • MyBatis中的模糊查询语句

    其实就只有一条sql语句 <select id = "search" resultType = "material"> select material_id,material_num,material_name,material_type,material_model,id from material where material_name like '%${value}%' or material_num like '%${value}%' </

  • Mybatis中的like模糊查询功能

    1.  参数中直接加入%% param.setUsername("%CD%"); param.setPassword("%11%"); <select id="selectPersons" resultType="person" parameterType="person"> select id,sex,age,username,password from person where true &

  • Oracle在Mybatis中SQL语句的配置方法

    数据库中有下划线的字段在实体中应采用驼峰命名法,如P_NAME对应pName,实例如下: 1.XML文件中SQL语句配置(Geteway.xml文件) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-m

  • mybatis中使用oracle关键字出错的解决方法

    发现问题 最近发现在mybatis中如果使用的字段是Oracle的关键字,会出现错误,通过查找相关的资料终于解决了,下面来一起看看详细的解决方法: 解决方式 可以使用resultMap** 示例代码 <!-- 预约日历用 --> <resultMap id="FullDateResultMap" type="com.haier.hrssc.reserveschedual.dto.FullcalendaDTO"> <id property

  • 浅谈mybatis中的#和$的区别 以及防止sql注入的方法

    mybatis中的#和$的区别 1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id". 2. $将传入的数据直接显示生成在sql中.如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id,  如果传入的

  • mybatis 中 foreach collection的用法小结(三种)

    foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有 item,index,collection,open,separator,close. item表示集合中每一个元素进行迭代时的别名,     index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,     open表示该语句以什么开始,     separator表示在每次进行迭代之间以什么符号作为分隔 符,     close表示以什么结束. 在使用foreach的时候

随机推荐