环境:springboot2.6.12 + MyBatis3.5.6 + MySQL
MyBatis是一种优秀的持久层框架,它支持自定义类型转换和数据加密解密。通过自定义类型转换,你可以轻松地将数据库中的数据类型转换为Java对象中的数据类型,以及将Java对象中的数据类型转换为数据库中的数据类型。而数据加密解密则可以提高数据的安全性,保护敏感信息不被泄露。在MyBatis中,你可以使用类型处理器(TypeHandler)来实现自定义类型转换,使用加密和解密算法来实现数据加密解密。
本案例使用自定义类型转换器对数据列进行加解密
1. 依赖及相关配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testjpa?serverTimezone=GMT%2B8
username: root
password: xxxxx
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimumIdle: 10
maximumPoolSize: 200
autoCommit: true
idleTimeout: 30000
poolName: MasterDatabookHikariCP
maxLifetime: 1800000
connectionTimeout: 30000
connectionTestQuery: SELECT 1
---
spring:
jpa:
generateDdl: false
hibernate:
ddlAuto: update
openInView: true
show-sql: true
---
pagehelper:
helperDialect: mysql
reasonable: true
pageSizeZero: true
offsetAsPageNum: true
rowBoundsWithCount: true
---
mybatis:
type-aliases-package: com.pack.domain
mapper-locations:
- classpath:/mappers/*.xml
configuration:
lazy-loading-enabled: true
aggressive-lazy-loading: false
---
logging:
level:
com.pack.mapper: debug
实体对象
@Entity
@Table(name = "BC_PERSON")
public class Person extends BaseEntity {
private String name ;
private String idNo ;
}
这里是用JPA来帮助我们生成数据表。
2. 自定义类型转换器及数据加解密工具
public class EncryptTypeHandler implements TypeHandler<String> {
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, EncryptUtils.encrypt(parameter)) ;
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName) ;
if (value == null || value.length() == 0) {
return null ;
}
return EncryptUtils.decrypt(value);
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex) ;
if (value == null || value.length() == 0) {
return null ;
}
return EncryptUtils.decrypt(value);
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex) ;
if (value == null || value.length() == 0) {
return null ;
}
return EncryptUtils.decrypt(value);
}
}
加解密工具类
public class EncryptUtils {
private static final String secretKey = "1111222244445555" ;
private static final String ALGORITHM = "AES" ;
public static String encrypt(String data) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding") ;
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secretKey.getBytes(), ALGORITHM)) ;
return Hex.encode(cipher.doFinal(data.getBytes())) ;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
return null ;
}
}
public static String decrypt(String secretText) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding") ;
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey.getBytes(), ALGORITHM)) ;
return new String(cipher.doFinal(Hex.decode(secretText))) ;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
return null ;
}
}
private static class Hex {
private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public static byte[] decode(CharSequence s) {
int nChars = s.length();
if (nChars % 2 != 0) {
throw new IllegalArgumentException("16进制数据错误");
}
byte[] result = new byte[nChars / 2];
for (int i = 0; i < nChars; i += 2) {
int msb = Character.digit(s.charAt(i), 16);
int lsb = Character.digit(s.charAt(i + 1), 16);
if (msb < 0 || lsb < 0) {
throw new IllegalArgumentException("Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
}
result[i / 2] = (byte) ((msb << 4) | lsb);
}
return result;
}
public static String encode(byte[] buf) {
StringBuilder sb = new StringBuilder() ;
for (int i = 0, leng = buf.length; i < leng; i++) {
sb.append(HEX[(buf[i] & 0xF0) >>> 4]).append(HEX[buf[i] & 0x0F]) ;
}
return sb.toString() ;
}
}
}
Mapper及XML文件
@Mapper
public interface PersonMapper {
List<Person> queryPersons() ;
int insertPerson(Person person) ;
}
<mapper namespace="com.pack.mapper.PersonMapper">
<resultMap type="com.pack.domain.Person" id="PersonMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="id_no" property="idNo" typeHandler="com.pack.mybatis.EncryptTypeHandler"/>
<result column="create_time" property="createTime"/>
</resultMap>
<select id="queryPersons" resultMap="PersonMap">
SELECT * FROM bc_person
</select>
<insert id="insertPerson" parameterType="com.pack.domain.Person">
insert into bc_person (id, name, id_no, create_time) values (#{id}, #{name}, #{idNo, typeHandler=com.pack.mybatis.EncryptTypeHandler}, #{createTime})
</insert>
</mapper>
查询数据时在resultMap中的result中配置typeHandler="com.pack.mybatis.EncryptTypeHandler",指明该列的类型转换。
在insert中对具体的列进行指明类型转换。
3. 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootComprehensiveApplicationTests {
@Resource
private PersonMapper personMapper ;
@Test
public void testInsertMapper() {
com.pack.domain.Person person = new com.pack.domain.Person() ;
person.setId("0001") ;
person.setCreateTime(new Date()) ;
person.setIdNo("111111") ;
person.setName("中国") ;
personMapper.insertPerson(person) ;
}
@Test
public void testQueryUers() {
System.out.println(personMapper.queryPersons()) ;
}
}
图片
插入数据时数据已经被我们自定义的类型转换器进行了加密处理。
图片
查询数据进行了解密处理。
完毕!!!