前言
根据公司业务需要,灵活对客户敏感信息进行加解密,这里采用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); } } }
测试结果:
- 插入数据
- 查询数据:
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论