前言
根据公司业务需要,灵活对客户敏感信息进行加解密,这里采用mybatis拦截器进行简单实现个demo。
拦截器的使用
// 执行 executor (update, query, flushstatements, commit, rollback, gettransaction, close, isclosed) // 请求参数处理 parameterhandler (getparameterobject, setparameters) // 返回结果集处理 resultsethandler (handleresultsets, handleoutputparameters) // sql语句构建 statementhandler (prepare, parameterize, batch, update, query)
我们要实现数据加密,进入数据库的字段不能是真实的数据,但是返回来的数据要真实可用,所以我们需要针对 parameter 和 resultset 两种类型处理,同时为了更灵活的使用,我们需要自定义注解。
/**
*需要加解密的字段注解
**/
@target({elementtype.field})
@retention(retentionpolicy.runtime)
@documented
public @interface encryption {
string encryptiontype() default "";
}
编写一下加解密算法(随便找的)
**
* @desc: aes对称加密,对明文进行加密、解密处理
* @author:
* @createtime: 20231014 上午9:54:52
* @version: v0.0.1
*/
public class aesutil {
private static final string key_algorithm = "aes";
private static final string cipher_algorithm = "aes/ecb/pkcs5padding";
/**
* @desc: aes对称-加密操作
* @version: v0.0.1
* @param keystr 进行了base64编码的秘钥
* @param data 需要进行加密的原文
* @return string 数据密文,加密后的数据,进行了base64的编码
*/
public static string encrypt(string keystr, string data) throws exception {
// 转换密钥
key key = new secretkeyspec(base64.getdecoder().decode(keystr), key_algorithm);
cipher cipher = cipher.getinstance(cipher_algorithm);
// 加密
cipher.init(cipher.encrypt_mode, key);
byte[] result = cipher.dofinal(data.getbytes());
return base64.getencoder().encodetostring(result);
}
/**
* @desc: aes对称-解密操作
* @version: v0.0.1
* @param keystr 进行了base64编码的秘钥
* @param data 需要解密的数据<span style="color:red;">(数据必须是通过aes进行加密后,对加密数据base64编码的数据)</span>
* @return string 返回解密后的原文
*/
public static string decrypt(string keystr, string data) throws exception {
// 转换密钥
key key = new secretkeyspec(base64.getdecoder().decode(keystr), key_algorithm);
cipher cipher = cipher.getinstance(cipher_algorithm);
// 解密
cipher.init(cipher.decrypt_mode, key);
byte[] result = cipher.dofinal(base64.getdecoder().decode(data));
return new string(result);
}
/**
* @desc: 生成aes的秘钥,秘钥进行了base64编码的字符串
* @version: v0.0.1
* @return string 对生成的秘钥进行了base64编码的字符串
*/
public static string keygenerate() throws exception {
// 生成密钥
keygenerator keygenerator = keygenerator.getinstance(key_algorithm);
keygenerator.init(new securerandom());
secretkey secretkey = keygenerator.generatekey();
byte[] keybytes = secretkey.getencoded();
return base64.getencoder().encodetostring(keybytes);
}
public static void main(string[] args) throws exception {
system.out.println(
keygenerate()
);
}
}
编写一下加解密接口
/**
* 加解密处理接口
*/
public interface cipherhandler {
/**
* 加密
* @param data 需要加密的数据
* @return 加密结果
*/
string encrypt(string data) throws exception;
/**
* 解密
* @param data 需要加密的数据
* @return 解密结果
*/
string decrypt(string data) throws exception;
}
public class aescipher implements cipherhandler{
private final static string keystr="glrwfsbckzppyvqiwt8m/q==";
@override
public string encrypt(string data) throws exception {
return aesutil.encrypt(this.keystr,data);
}
@override
public string decrypt(string data) throws exception {
return aesutil.decrypt(this.keystr,data);
}
public static cipherhandler getaescipher(){
return new aescipher();
}
}
接下来写一下加解密的拦截器
@intercepts({
@signature(type = parameterhandler.class, method = "setparameters", args = preparedstatement.class),
})
@component
@slf4j
public class parameterinterceptor implements interceptor {
private cipherhandler cipherhandler = aescipher.getaescipher();
@override
public object intercept(invocation invocation) throws throwable {
defaultparameterhandler parameterhandler = (defaultparameterhandler) invocation.gettarget();
// 获取参数对像,即 mapper 中 paramstype 的实例
field parameterfield = parameterhandler.getclass().getdeclaredfield("parameterobject");
parameterfield.setaccessible(true);
// 取出实例
object parameterobject = parameterfield.get(parameterhandler);
try {
// 搜索该方法中是否有需要加密的字段
list<field> fieldlist = searchparamannotation(parameterhandler);
//加密
if(!commonhelper.isempty(fieldlist)){
dealparamencrypt(fieldlist,parameterobject);
}
parameterfield.set(parameterhandler, parameterobject);
preparedstatement ps = (preparedstatement) invocation.getargs()[0];
parameterhandler.setparameters(ps);
} catch (exception e) {
log.info(e.getmessage());
}
return invocation.proceed();
}
/**
* 查找需要需要加密字段
* @param parameterhandler
* @return
* @throws exception
*/
private list<field> searchparamannotation(parameterhandler parameterhandler) throws exception {
class<defaultparameterhandler> handlerclass = defaultparameterhandler.class;
field mappedstatementfiled = handlerclass.getdeclaredfield("mappedstatement");
mappedstatementfiled.setaccessible(true);
mappedstatement mappedstatement = (mappedstatement) mappedstatementfiled.get(parameterhandler);
string methodname = mappedstatement.getid();
// 获取mapper类对象
class<?> mapperclass = class.forname(methodname.substring(0, methodname.lastindexof('.')));
methodname = methodname.substring(methodname.lastindexof('.') + 1);
method[] methods = mapperclass.getdeclaredmethods();
method method = null;
for (method m : methods) {
if (m.getname().equals(methodname)) {
method = m;
break;
}
}
list<field> fieldlist = new arraylist<>();
if (method != null) {
annotation[][] pa = method.getparameterannotations();
parameter[] parameters = method.getparameters();
for (int i = 0; i < pa.length; i++) {
parameter parameter = parameters[i];
string typename = parameter.getparameterizedtype().gettypename();
// 去除泛型导致的classnotfoundexception
class<?> parameterclass = class.forname(typename.contains("<") ? typename.substring(0, typename.indexof("<")) : typename);
field[] declaredfields = parameterclass.getdeclaredfields();
for (field declaredfield : declaredfields) {
annotation annotation = declaredfield.getannotation(encryption.class);
if(!commonhelper.isempty(annotation))fieldlist.add(declaredfield);
}
}
}
return fieldlist;
}
/**
* 处理加密类
* @param fields
* @param parameterobject
* @throws exception
*/
private void dealparamencrypt(list<field> fields,object parameterobject) throws exception {
fields.foreach(declaredfield->{
declaredfield.setaccessible(true);
object o = null;
try {
o = declaredfield.get(parameterobject);
declaredfield.set(parameterobject,cipherhandler.encrypt(o.tostring()));
} catch (exception e) {
throw new runtimeexception(e);
}
});
}
@override
public object plugin(object target) {
return plugin.wrap(target,this);
}
@override
public void setproperties(properties properties) {
}
}
查询结果解密
@intercepts({
@signature(type = resultsethandler.class, method = "handleresultsets", args = {statement.class})
})
@component
public class resultsetinterceptor implements interceptor {
private cipherhandler cipherhandler = aescipher.getaescipher();
@override
public object intercept(invocation invocation) throws throwable {
// 取出查询的结果
object resultobject = invocation.proceed();
if (objects.isnull(resultobject)) {
return null;
}
// 基于selectlist
if (resultobject instanceof list<?>) {
list<?> resultlist = (list<?>) resultobject;
if (!commonhelper.isempty(resultlist)) {
for (object obj : resultlist) {
todecrypt(obj);
}
}
} else {
todecrypt(resultobject);
}
return resultobject;
}
private void todecrypt(object object) throws exception {
class<?> objectclass = object.getclass();
field[] declaredfields = objectclass.getdeclaredfields();
for (field declaredfield : declaredfields) {
annotation annotation = declaredfield.getannotation(encryption.class);
if(!commonhelper.isempty(annotation)){
declaredfield.setaccessible(true);
declaredfield.set(object,cipherhandler.decrypt(declaredfield.get(object).tostring()));
}
}
}
@override
public object plugin(object o) {
return plugin.wrap(o,this);
}
@override
public void setproperties(properties properties) {
}
}
判空工具类
public class commonhelper {
public static boolean isempty(object o){
if(o==null)return true;
if(o instanceof string){
return ((string) o).isempty();
}else if(o instanceof list){
return collectionutils.isempty((list) o) || ((list<?>) o).size()==0 ;
}else if(o instanceof map){
return collectionutils.isempty((map) o) || ((map<?,?>) o).size()==0 ;
}else {
return objects.isnull(o);
}
}
}
测试结果:
- 插入数据


- 查询数据:

总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论