1. Background
Some projects need to encrypt some information before storage, for data security or privacy compliance, but at the same time, it also makes data processing cumbersome, which will inevitably lead to repetitive and lengthy codes. If the data can be processed in the persistence layer and avoided in the business layer, this problem can be reasonably avoided.
Two, the program
Use the TypeHandler provided by the mybatis framework to process data in the persistence layer.
Introduction to TypeHandler
Typehandler is an interface provided by mybatis. By implementing this interface, the conversion between jdbc type data and java type data can be realized. The conversion of varchar to string and bigint to long that we often see are all handled by mybatis itself.
We can implement a Typehandler ourselves to meet our needs.
3. Detailed implementation
1. Implement the interface and define your own Typehandler
Generally, you can implement the BaseTypeHandler interface. The author's encryption and decryption use the des encryption provided by hutool, and the maven coordinates are as follows:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.10</version> </dependency>
The code is as follows, and the method can be written according to the business requirements to realize the code:
package com.example.cryptotypehandler.common; import cn.hutool.crypto.symmetric.SymmetricAlgorithm; import cn.hutool.crypto.symmetric.SymmetricCrypto; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @Slf4j @MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(String.class) public class CryptoTypeHandler extends BaseTypeHandler<String> { private final byte[] key = {-26, -70, -29, -99, 73, -82, 91, -50, 79, -77, 59, 104, 2, -36, 50, -22, -39, -15, -57, -89, 81, -99, 42, -89}; private final SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DESede, key); /* * Processing ginseng */ @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { if (parameter != null) { //encryption String encryptHex = des.encryptHex(parameter); log.info("{} ---encrypted as---> {}", parameter, encryptHex); ps.setString(i, encryptHex); } } /* * Get the return result according to the column name, you can process the return value in this method */ @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { String originRes = rs.getString(columnName); if (originRes != null) { String res = des.decryptStr(originRes); log.info("{} ---decrypted as---> {}", originRes, res); return res; } log.info("The result is empty, no need to decrypt"); return null; } /* * Get the return result according to the subscript of the column, you can process the return value in this method */ @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String originRes = rs.getString(columnIndex); if (originRes != null) { String res = des.decryptStr(originRes); log.info("{} ---decrypted as---> {}", originRes, res); return res; } log.info("The result is empty, no need to decrypt"); return null; } /* * Obtain the return result (stored procedure) according to the column subscript, and the return value can be processed in this method */ @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String originRes = cs.getString(columnIndex); if (originRes != null) { String res = des.decryptStr(originRes); log.info("{} ---decrypted as---> {}", originRes, res); return res; } log.info("The result is empty, no need to decrypt"); return null; } }
2. Register a custom TypeHandler
The written TypeHandler needs to be registered in mybatis, and the configuration is added in application.yml or application.properties:
properties file:
mybatis.type-handlers-package=com.example.cryptotypehandler.common
yml file
mybatis: type-handlers-package: com.example.cryptotypehandler.common
The author's package structure is as follows
3. Define mapper layer interface
entity object class
package com.example.cryptotypehandler.domain; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Getter @Setter @ToString public class AccountDO { private Long id; /** * username */ private String userName; /** * password */ private String password; }
It is no different from ordinary mapper:
package com.example.cryptotypehandler.mapper; import com.example.cryptotypehandler.domain.AccountDO; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** * <p> * Mapper interface * </p> * * @author shuai.mh * @since 2022-11-29 */ @Mapper public interface AccountMapper { int insertEncrypt(AccountDO accountDO); List<AccountDO> selectAccount(AccountDO accountDO); }
4. Write mapper.xml
Here are a few points to note:
- First look at the insert statement, what we need to encrypt is the password field, so add typeHandler=com.example.cryptotypehandler.common.CryptoTypeHandler after password; in addition, in #{userName}, do not fill in jdbcType=varchar, because the custom typerHandler The following two annotations are added: @MappedJdbcTypes(JdbcType.VARCHAR) and @MappedTypes(String.class). These two annotations indicate that if the JdbcType is varchar, this handler will be used. If added, userName will also be encrypted.
- Looking at the select statement again, there is no difference between sql and ordinary ones, but the password mapping in the resultMap adds typeHandler="com.example.cryptotypehandler.common.CryptoTypeHandler", which means that this field will be processed by the handler when it is converted into an entity object, and other The mapping jdbcType of the field remains the default, and if it is varchar, it will also be processed.
<?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.example.cryptotypehandler.mapper.AccountMapper"> <!-- Generic Query Mapping Results --> <resultMap id="BaseResultMap" type="com.example.cryptotypehandler.domain.AccountDO"> <id column="id" property="id" /> <result column="user_name" property="userName"/> <result column="password" property="password" typeHandler="com.example.cryptotypehandler.common.CryptoTypeHandler" /> </resultMap> <!-- Common Query Result Columns --> <sql id="Base_Column_List"> id, user_name, password </sql> <insert id="insertEncrypt"> insert into account (id, user_name, password) values (#{id}, #{userName}, #{password, typeHandler=com.example.cryptotypehandler.common.CryptoTypeHandler}) </insert> <select id="selectAccount" resultMap="BaseResultMap"> select <include refid="Base_Column_List"></include> from account where user_name = #{userName} </select> </mapper>
5. Call interface, simple test
①Add new user
② Check the database, the password has been encrypted
③Query the user, the query result has been decrypted
Thanks for reading, welcome to communicate! Ask for attention (〃'▽'〃)