文章目录
前言
在日常开发中我们可能会处理一些敏感信息,例如存储个人信息。这时候我们便需要对数据进行加解密。一般的流程是在存入数据库前对数据进行加密,在查询数据后对数据进行解密然后传给前端。但是如果每一次存取操作都要加解密都要手动操作的话就太过麻烦了,因此我们需要编写代码让ROM框架可以自动处理这些过程。
提示:以下是本篇文章正文内容,下面案例可供参考
一、编写加解密工具类
加解密的方法有很多,我这里只是举一个例子,自己平时喜欢用哪种就用哪种就可以了。
示例:我自己使用的是DES。这里是定义了一个加解密的接口,然后不同加解密的算法实现接口即可。
public interface CryptoHandler {
String encrypt(String data) throws Exception;
String decrypt(String encryptedData) throws Exception;
}
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
/**
* @author Namego
* @date 2023/6/23 13:55
*/
public class DESedeCryptHandler implements CryptoHandler {
private static final String ENCRYPTION_ALGORITHM = "DESede"; // 使用TripleDES算法
private static final String KEY = "yourSecretKey";
@Override
public String encrypt(String data) throws Exception {
if(data == null){
return data;
}
SecretKeySpec secretKey = generateSecretKey(KEY);
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
@Override
public String decrypt(String encryptedData) throws Exception {
if(encryptedData == null){
return encryptedData;
}
SecretKeySpec secretKey = generateSecretKey(KEY);
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
private static SecretKeySpec generateSecretKey(String key) throws Exception {
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashedKey = md.digest(keyBytes);
byte[] truncatedKey = new byte[24];
System.arraycopy(hashedKey, 0, truncatedKey, 0, truncatedKey.length);
return new SecretKeySpec(truncatedKey, ENCRYPTION_ALGORITHM);
}
}
二、使用Mybatis
1.引入库
库就是mybatis的那些依赖,这里就不一一列举了。
2.编写Handler继承Mybatis的BaseTypeHandler
这里我是使用String类型,因为加解密的数据都是字符串,你可以尝试使用Object去处理。注意代码里的无参构造函数
代码如下(示例):
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author Namego
* @date 2023/6/27 15:31
*/
public class EncryptionTypeHandler extends BaseTypeHandler<String> {
private static CryptoHandler deSedeCryptHandler = new DESedeCryptoHandler();
/**
* 这里无参构造函数我写出来是因为要强调这里必须要有无参构造函数,不然mybatis框架会报错。如果你自己定义了有参数的构造函数,一定要声明无参构造
*/
public EncryptionTypeHandler() {
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
// 在写入数据库之前对字段进行加密
String encryptedValue = null;
try {
encryptedValue = deSedeCryptHandler.encrypt(parameter);
} catch (Exception e) {
e.printStackTrace();
}
ps.setString(i, encryptedValue);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 在从数据库读取字段值后对字段进行解密
String encryptedValue = rs.getString(columnName);
if (encryptedValue == null) {
return null;
}
String decryptedValue = null;
try {
decryptedValue = deSedeCryptHandler.decrypt(encryptedValue);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedValue;
}
@Override
public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
// 实现解密逻辑
String encryptedValue = resultSet.getString(i);
if (encryptedValue == null) {
return null;
}
String decryptedValue = null;
try {
decryptedValue = deSedeCryptHandler.decrypt(encryptedValue);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedValue;
}
@Override
public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
// 实现解密逻辑
String encryptedValue = callableStatement.getString(i);
if (encryptedValue == null) {
return null;
}
String decryptedValue = null;
try {
decryptedValue = deSedeCryptHandler.decrypt(encryptedValue);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedValue;
}
}
3、在你需要加解密的字段上添加注解
在字段上添加 @TableField(typeHandler = EncryptionTypeHandler.class)注解
示例代码如下:
/**
* @author Namego
* @date 2023/6/22 16:25
*/
@Data
@Accessors(chain = true)
@TableName("stu_user")
public class StuUserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@TableId
private Long userId;
/**
* 学号
*/
@NotBlank(message = "学号不能为空",groups = UpdateGroup.class)
@TableField(typeHandler = EncryptionTypeHandler.class)
private String schoolNumber;
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空",groups = UpdateGroup.class)
@Size(message = "姓名长度不得小于2",min = 2)
@TableField(typeHandler = EncryptionTypeHandler.class)
private String name;
/**
* QQ号
*/
@NotBlank(message = "QQ号不能为空",groups = UpdateGroup.class)
@Size(message = "QQ长度不能超过13",max = 13)
@TableField(typeHandler = EncryptionTypeHandler.class)
private String qq;
}
这样即可在存取数据的时候进行加密了。但查询的时候mybatis不会自动走这个加解密的handler,我们需要自己在xml上写好
4、查询时也使用自定义的handler
在你需要解密的字段后添加typeHandler的内容,注意这里的handler包路径要改成你自己的路径。
示例配置如下:
<resultMap id="BaseResultMap" type="com.namego.model.stu.StuUserEntity">
<result property="schoolNumber" column="school_number" typeHandler="com.namego.handler.EncryptionTypeHandler" />
<result property="name" column="name" typeHandler="com.namego.handler.EncryptionTypeHandler" />
<result property="qq" column="qq" typeHandler="com.namego.handler.EncryptionTypeHandler" />
</resultMap>
三、使用SpringJpa
1、引入库
就导入jpa操作的依赖,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2、编写Converter实现AttributeConverter方法
在jpa中使用的是Converter,跟上面Mybatis里的Handler道理一样。
示例代码如下:
@Converter
public class CryptoConverter implements AttributeConverter<String, String> { //这里是字符串转字符串,如果你是Integer转字符串就是<Integer,String>
private static CryptoHandler deSedeCryptHandler = new DESedeCryptoHandler();
/**
* 存入数据库前进行的转化操作
* @param attribute
* @return
*/
@Override
public String convertToDatabaseColumn(String attribute) {
String encryptedValue = null;
try {
encryptedValue = deSedeCryptHandler.encrypt(attribute);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedValue;
}
/**
* 取出数据库后进行的转化操作
* @param dbData
* @return
*/
@Override
public String convertToEntityAttribute(String dbData) {
String decryptedValue = null;
try {
decryptedValue = deSedeCryptHandler.decrypt(dbData);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedValue;
}
}
3、在你需要加解密的字段上添加注解
在字段上添加 @TableField(typeHandler = EncryptionTypeHandler.class)注解
示例代码如下:
/**
* @author Namego
* @date
*/
@Data
@Accessors(chain = true)
@Entity
public class StuUserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@Id
private Long userId;
/**
* 学号
*/
@NotBlank(message = "学号不能为空",groups = UpdateGroup.class)
@Convert(converter = CryptoConverter.class)
private String schoolNumber;
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空",groups = UpdateGroup.class)
@Size(message = "姓名长度不得小于2",min = 2)
@Convert(converter = CryptoConverter.class)
private String name;
/**
* QQ号
*/
@NotBlank(message = "QQ号不能为空",groups = UpdateGroup.class)
@Size(message = "QQ长度不能超过13",max = 13)
@Convert(converter = CryptoConverter.class)
private String qq;
}
jpa的话在添加数据和存储数据都会自动走这个Converter,就不用跟Mybatis的xml里添加了。
总结
在我自己实际开发中,我使用mybatis-plus自带的selectById方法他是没法走Handler的,在测试了很多方法都不行,最后只能自己写查询方法,如果哪位大佬知道如何解决还希望提一提。