注解方式实现数据库字段加密与解密

目录

  • 前言
  • 实现步骤
    • 定义注解
    • 加密工具类
    • 定义mybatis拦截器
  • 总结

前言

一些敏感信息存入数据需要进行加密处理,比如电话号码,身份证号码等,从数据库取出到前端展示时需要解密,如果分别在存入取出时去做处理,会很繁锁,至此,我查了很多相关资料,最后得到一个比较完美的解决方案。

实现步骤

定义注解

1、实体注解@SensitiveEntity

import java.lang.annotation.*;@Target({ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SensitiveEntity {}

2、字段注解@SensitiveEntity

import java.lang.annotation.*;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SensitiveField {}

加密工具类

AesFieldUtils.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.regex.Matcher;
import java.util.regex.Pattern;@Slf4j
@Component
public class AesFieldUtils {/*** 加密算法*/private final String KEY_ALGORITHM = "AES";/*** 算法/模式/补码方式*/private final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";/*** 编码格式*/private final String CODE = "utf-8";/*** base64验证规则*/private static final String BASE64_RULE = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)=?$";/*** 正则验证对象*/private static final Pattern PATTERN = Pattern.compile(BASE64_RULE);/*** 加解密 密钥key*/@Value("${aes.key}")private String key;/*** @param content 加密字符串* @return 加密结果*/public String encrypt(String content) {return encrypt(content, key);}/*** 加密** @param content 加密参数* @param key     加密key* @return 结果字符串*/public String encrypt(String content, String key) {//判断如果已经是base64加密字符串则返回原字符串if (isBase64(content)) {return content;}// 为了安全起见,暂时不加密,需要时再放开//return content;byte[] encrypted = encrypt2bytes(content, key);if (null == encrypted || encrypted.length < 1) {log.error("加密字符串[{}]转字节为null", content);return null;}return Base64Utils.encodeToString(encrypted);}/*** @param content 加密字符串* @param key     加密key* @return 返回加密字节*/public byte[] encrypt2bytes(String content, String key) {try {byte[] raw = key.getBytes(CODE);SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);return cipher.doFinal(content.getBytes(CODE));} catch (Exception e) {log.error("failed to encrypt: {} of {}", content, e);return null;}}/*** @param content 加密字符串* @return 返回加密结果*/public String decrypt(String content) {try {return decrypt(content, key);} catch (Exception e) {log.error("failed to decrypt: {}, e: {}", content, e);return null;}}/*** 解密** @param content 解密字符串* @param key     解密key* @return 解密结果*/public String decrypt(String content, String key) throws Exception {//不是base64格式字符串则不进行解密if (!isBase64(content)) {return content;}// 为了安全起见,暂时不加密解密,需要时再放开//return content;return decrypt(Base64Utils.decodeFromString(content), key);}/*** @param content 解密字节* @param key     解密key* @return 返回解密内容*/public String decrypt(byte[] content, String key) throws Exception {if (key == null) {log.error("AES key should not be null");return null;}byte[] raw = key.getBytes(CODE);SecretKeySpec keySpec = new SecretKeySpec(raw, KEY_ALGORITHM);Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, keySpec);try {byte[] original = cipher.doFinal(content);return new String(original, CODE);} catch (Exception e) {log.error("failed to decrypt content: {}/ key: {}, e: {}", content, key, e);return null;}}/*** 判断是否为 base64加密** @param str 参数* @return 结果*/public static boolean isBase64(String str) {Matcher matcher = PATTERN.matcher(str);return matcher.matches();}}

其中aes.key为加密key随便填

定义mybatis拦截器

MyBatisInterceptor.java

import cn.hutool.core.util.ReflectUtil;
import com.hw.common.annotation.SensitiveEntity;
import com.hw.common.service.AesService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Slf4j
public class MyBatisInterceptor implements Interceptor {@Resourceprivate AesService aesService;@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object target = invocation.getTarget();//拦截sql结果处理器if (target instanceof ResultSetHandler) {return resultDecrypt(invocation);}//拦截sql参数处理器if (target instanceof ParameterHandler) {return parameterEncrypt(invocation);}//拦截sql语句处理器if (target instanceof StatementHandler) {return replaceSql(invocation);}return invocation.proceed();}/*** 对mybatis映射结果进行字段解密** @param invocation 参数* @return 结果* @throws Throwable 异常*/private Object resultDecrypt(Invocation invocation) throws Throwable {//取出查询的结果Object resultObject = invocation.proceed();if (Objects.isNull(resultObject)) {return null;}//基于selectListif (resultObject instanceof ArrayList) {ArrayList resultList = (ArrayList) resultObject;if (CollectionUtils.isEmpty(resultList)) {return resultObject;}for (Object result : resultList) {if (needToDecrypt(result)) {//逐一解密aesService.decrypt(result);}}//基于selectOne} else {if (needToDecrypt(resultObject)) {aesService.decrypt(resultObject);}}return resultObject;}/*** mybatis映射参数进行加密** @param invocation 参数* @return 结果* @throws Throwable 异常*/private Object parameterEncrypt(Invocation invocation) throws Throwable {//@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler//若指定ResultSetHandler ,这里则能强转为ResultSetHandlerParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();// 获取参数对像,即 mapper 中 paramsType 的实例Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");parameterField.setAccessible(true);//取出实例Object parameterObject = parameterField.get(parameterHandler);if(parameterHandler.getParameterObject() instanceof MapperMethod.ParamMap){MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameterHandler.getParameterObject();parameterObject = paramMap.get("param1");}if (null == parameterObject) {return invocation.proceed();}Class<?> parameterObjectClass = parameterObject.getClass();//校验该实例的类是否被@SensitiveEntity所注解SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveEntity.class);//未被@SensitiveEntity所注解 则为nullif (Objects.isNull(sensitiveEntity)) {return invocation.proceed();}//取出当前当前类所有字段,传入加密方法//Field[] declaredFields = parameterObjectClass.getDeclaredFields();Field[] allFields = ReflectUtil.getFields(parameterObjectClass);aesService.encrypt(allFields, parameterObject);return invocation.proceed();}/*** 替换mybatis Sql中的加密Key** @param invocation 参数* @return 结果* @throws Throwable 异常*/private Object replaceSql(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();//获取到原始sql语句String sql = boundSql.getSql();if (null == sql){return invocation.proceed();}//通过反射修改sql语句Field field = boundSql.getClass().getDeclaredField("sql");field.setAccessible(true);field.set(boundSql, sql);return invocation.proceed();}/*** 判断是否包含需要加解密对象** @param object 参数* @return 结果*/private boolean needToDecrypt(Object object) {if(Objects.isNull(object)){return false;}Class<?> objectClass = object.getClass();Class<?> parentClass = objectClass.getSuperclass();SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(objectClass, SensitiveEntity.class);SensitiveEntity parentSensitiveEntity = AnnotationUtils.findAnnotation(parentClass, SensitiveEntity.class);return Objects.nonNull(sensitiveEntity) || Objects.nonNull(parentSensitiveEntity);}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}}

总结

mybatis拦截器非常强大,它可以对所有存入数据库的数据进行处理,包括更新,插入,查询和删除…,对数据做统一处理非常方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/4852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何下载SRA存放在AWS的原始数据

通常&#xff0c;我们都是利用prefetch从NCBI上获取数据&#xff0c;然后用fasterp-dump/fastq-dump 转成fastq。但遗憾的SRA的数据是原数据的有损压缩&#xff0c;比如说我19年参与发表的文章里单细胞数据上传的是3个文件&#xff0c;但是当时的faster-dump/fastq-dump只能拆出…

【ArcGIS Pro二次开发】(46):要素类从上到下、从左到右排序

要素类经过编辑之后&#xff0c;【OBJECTID】字段会变得不规律。应部分网友要求&#xff0c;做了这个从上到下、从左到右排序的工具。 不过后来在ArcGIS Pro中发现了一个【排序】工具&#xff0c;已经可以完美实现这个功能需求&#xff0c;发现自己做了个白工。 不过做了不能白…

Ghost Buster Pro for mac(快速清理卸载的应用残存文件)

Ghost Buster Pro for mac可从您已卸载的应用程序中查找并删除文件。该应用程序速度快如闪电&#xff0c;可立即释放内存。 许多应用程序都安装在计算机上&#xff0c;但它们通常只会在您的计算机上停留很短的时间。每个应用程序都会创建文件&#xff0c;但删除应用程序不会删…

若依字典使用

若依字典使用 此文章使用的若依是大于3.7.0版本的 JS文件配置 main.js中引入全局变量和方法 import DictData from /components/DictData DictData.install()DictData.js配置 可以从DictData.js中看出在install方法中调用了字典查询接口&#xff0c;在install方法中可以做…

前端 | (五)CSS三大特性及常用属性 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 文章目录 &#x1f4da;CSS三大属性&#x1f407;层叠性&#x1f407;继承性&#x1f407;优先级 &#x1f4da;CSS常用属性&#x1f407;像素的概念&#x1f407;颜色的表示⭐️表…

火狐安卓版支持油猴了!后面将支持更多扩展插件

日前火狐浏览器每夜构建版的安卓版已经带来了更多扩展程序支持&#xff0c;这其中就包括大名鼎鼎的油猴扩展程序。本次火狐浏览器每夜构建版更新新增五款扩展程序支持&#xff0c;并且按照谋智基金会说法还会支持更多的扩展程序。 下载地址&#xff1a;https://ftp.mozilla.org…

【论文阅读】关于图像复杂度的论文

1997-An image embedding in image by a complexity based region segmentation method&#xff1a; 根据位平面的&#xff08;边缘/图像像素数&#xff09;计算复杂度 2012-https://journals.sagepub.com/doi/abs/10.1068/p6987&#xff1a; 对于象形文字&#xff0c;当将复…

WEB:FlatScience

背景知识 sql注入 SQLite数据库知识 SQLite3注入方法 题目 用dirsearch进行扫描&#xff0c;下面几个关键目录&#xff1a;robots.txt&#xff0c;login.php&#xff0c;admin.php&#xff0c;剩下的目录就是一些pdf格式的论文了 一个一个访问并查看源代码&#xff0c;在查看l…

ThinkPHP使用having时,同时使用分页会报错的解决方法

当使用ThinkPHP的分页查询时&#xff0c;如果与having同时使用&#xff0c;可能会出现错误。这是因为having会影响分页查询的结果。 解决方法是&#xff0c;先不要使用分页查询&#xff0c;先查询出所有数据&#xff0c;再使用array_slice函数对数据进行分页。示例代码如下&am…

【二分查找】275. H 指数 II

&#x1f4cd;前言 &#x1f57a;作者&#xff1a; 迷茫的启明星 学习路线C语言从0到1C初阶数据结构从0到1 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对…

模拟退火算法

先随机产生初始点&#xff0c;并获得函数值。接着循环随机产生定义域内的数&#xff0c;比较与现在所处的点的函数值大小&#xff0c;&#xff08;以求最大值为例&#xff09;若大于现在所处的点&#xff0c;那么就接受这个点。若小于这个点的值&#xff0c;不一定拒绝&#xf…

windows下安装consul、springboot整合consul

Spring Cloud Consul通过自动配置和绑定到Spring Environment和其他Spring编程模型习语&#xff0c;为Spring Boot应用程序提供Consul集成。通过一些简单的注解&#xff0c;可以快速启用和配置应用程序内的常用模式&#xff0c;并使用Hashicorp的Consul构建大型分布式系统。提供…

检测到错误页面web应用服务器版本信息泄露

详细描述 Web服务器未能正确处理异常请求导致Web服务器版本信息泄露&#xff0c;攻击者收集到服务器信息后可进行进一步针对性攻击。 解决办法 临时修复建议如下&#xff1a; 1、关闭web服务器错误提示。 2、关闭运行平台的错误提示。 3、建立错误机制&#xff0c;不要把真实…

Docker 的理解

docker 启停 秒级别&#xff0c;应用的处理。docker 如何快速启停&#xff0c;键值对&#xff0c;缓存&#xff0c;数据库。 hello redis web 反向代理 1 docker ps 查看进程 2 docker images 查看镜像 3 docker run hello-world 「先尝试从本地查找&#xff0c;本地没有&…

uniapp u-view 两个时间戳的差 倒计时

参考uView倒计时 1、常规操作&#xff1a;自定义倒计时组件 ui-count-down <template><view class"countdown-view"><u-count-down change"finishDown" :time"startTimeDown" :startTime"startTime" format"HH:…

MySQL八股学习总览-from 小林coding

MySQL八股学习总览-from 小林coding MySql执行流程连接MySQL服务器查询缓存解析SQL执行SQL预处理器优化器执行器 MySql执行流程 连接MySQL服务器 经过如下的命令,就可以与MySQL服务器建立起连接,三次握手 mysql -h$ip -u$user -p服务端查询多少个客户端连接 show processlis…

干货分享:商城系统开发方式

商城系统是一种为了满足电子商务需求而开发的系统&#xff0c;它能够实现在线购物、支付、订单管理等功能。在当今互联网时代&#xff0c;商城系统的开发方式多种多样。那么&#xff0c;商城系统开发方式有哪些呢&#xff1f; 1、完全独立自主开发 完全独立自主开发是指企业根…

【C++】仿函数(less)

C中的仿函数 class Solution { public:struct cmp{bool operator()(const pair<string,int>&kv1,const pair<string,int>&kv2){if(kv1.second<kv2.second) return true;if(kv1.secondkv2.second&&kv1.first>kv2.first) return true;return …

TCP/IP详解

目录 一、OSI参考模型 1.图示 2.OSI七层模型各自作用 3.七层通信过程 二、IP协议 1.IPv4首部 2.IPv6首部 三、TCP协议 1.tcp首部格式 2.握手挥手图示 3.握手流程 4.为什么要三次握手&#xff1f; 5.四次挥手流程 6.为什么要四次分手&#xff1f; 7.为什么要等待…