关于实现手机号,身份证号加密解密解决方案

Mybatis】基于Mybatis拦截器+注解,实现敏感数据自动加解密 我司最近在做等保,要求数据库里面的手机号、微信号、身份证号等等这些在数据库中都不能是明文,所以需要把数据库中的涉及到这些的加密保存,但是查询和保存的接口传参不受影响,之前业务给前端返回是什么样子依然保持不变,这样前端不受影响。 阅读详情

关于实现手机号,身份证号加密解密解决方案

要求:

1.不入侵任何业务逻辑
2.可以扩展解密解密字段
3.可以扩展加密解密算法

环境:

jdk1.8、springMVC

解决方案:

基于自定义注解和mybatis自定义拦截器实现

具体实现:

自定义注解 @SensitiveData ,@SensitiveField

主要作用是:可以扩展任何想要加密的字段,只要判断加密的实体类上是否有这两个注解即可
/**
* 注解敏感信息类的注解
*/
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {}


/**
* 注解敏感信息类中敏感字段的注解
*/
@Inherited
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {}

定义统一处理的加解密接口

package com.gisquest.platform.annotation;
import java.lang.reflect.Field;
public interface EncryptUtil {
      /*declaredFields:传入加密注解修改的字段
      * paramsObject:传入对应xml中paramterType对应的实例对象
      */
	<T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException;
}

package com.gisquest.platform.annotation;
public interface DecryptUtil {
      /*
      * result:传入对应xml中resultType对应的实例对象
      */
	<T> T decrypt(T result) throws IllegalAccessException;
}

定义统一处理的加解密接口的实现类

@Component
public class EncryptProcessor implements EncryptUtil{
	
@Value(value="${encrypt.algorithmType}")	
private String algorithmType; 
@Override
public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
	for (Field field : declaredFields) {
		//取出所有被注解的字段
		SensitiveField sensitiveField = field.getDeclaredAnnotation(SensitiveField.class);
		if (!Objects.isNull(sensitiveField)) {
			field.setAccessible(true);
			//获取该字段对应的value值
			Object object = field.get(paramsObject);
		    //暂时只实现String类型的加密
		    if (object instanceof String) {
			   String value = (String) object;
			   if(!StringUtils.isEmpty(algorithmType)) {
				   field.set(paramsObject, CommonAlgorithmUtils.encrypt(algorithmType,value)); 
			   }
             }
		  }
        }
	   return paramsObject;
   }
}

定义解密接口的实现类

@Component
public class DecryptProcessor implements DecryptUtil {

	@Value(value="${decrypt.algorithmType}")	
	private String algorithmType;
		
	@Override
	public <T> T decrypt(T result) throws IllegalAccessException {
		//取出resultType的类
		Class<?> resultClass = result.getClass();
		Field[] declaredFields = resultClass.getDeclaredFields();
		for (Field field : declaredFields) {
		//取出所有被EncryptDecryptField注解的字段
		SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
		if (!Objects.isNull(sensitiveField)) {
			field.setAccessible(true);
			Object object = field.get(result);
			//只支持String的解密
			if (object instanceof String) {
				String value = (String) object;
				  if(!StringUtils.isEmpty(algorithmType)) {
			        //对注解的字段进行逐一解密
			        field.set(result, CommonAlgorithmUtils.decrypt(algorithmType,value));
				  }
	       	   }
		   }
		}
		return result;
	}
}

定义加密时拦截器

package com.gisquest.platform.conf;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.core.annotation.AnnotationUtils;

import com.gisquest.platform.annotation.EncryptUtil;
import com.gisquest.platform.annotation.SensitiveData;


@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class) })
public class EncryptInterceptor implements Interceptor {
	private  EncryptUtil encryptUtil;
	public EncryptInterceptor(EncryptUtil encryptUtil) {
		this.encryptUtil = encryptUtil;
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
		// 获取参数对像,即 mapper 中 paramsType 的实例
		Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
		parameterField.setAccessible(true);
		// 取出实例
		Object parameterObject = parameterField.get(parameterHandler);
		if (parameterObject != null) {
			Class<?> parameterObjectClass = parameterObject.getClass();
			// 校验该实例的类是否被@SensitiveData所注解
			SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
			if (Objects.nonNull(sensitiveData)) {
				// 取出当前当前类所有字段,传入加密方法
				Field[] declaredFields = parameterObjectClass.getDeclaredFields();
				encryptUtil.encrypt(declaredFields, parameterObject);
			}
		}
		return invocation.proceed();
	}

	/**
	 * 切记配置,否则当前拦截器不会加入拦截器链
	 */
	@Override
	public Object plugin(Object o) {
		return Plugin.wrap(o, this);
	}

	// 自定义配置写入,没有自定义配置的可以直接置空此方法
	@Override
	public void setProperties(Properties properties) {
	}
}

定义解密时拦截器

package com.gisquest.platform.conf;

import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;
import com.gisquest.platform.annotation.DecryptUtil;
import com.gisquest.platform.annotation.SensitiveData;

@Intercepts({ @Signature(type = ResultSetHandler.class, method="handleResultSets", args=Statement.class) })
public class DecryptInterceptor implements Interceptor {

	private final DecryptUtil decryptUtil;

	DecryptInterceptor(DecryptUtil decryptUtil) {
		this.decryptUtil = decryptUtil;
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// 取出查询的结果
		Object resultObject = invocation.proceed();
		if (Objects.isNull(resultObject)) {
			return null;
		}
		// 基于selectList
		if (resultObject instanceof ArrayList) {
			ArrayList<?> resultList = (ArrayList<?>) resultObject;
			if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
				for (Object result : resultList) {
					// 逐一解密
					 decryptUtil.decrypt(result);
				}
			}
			// 基于selectOne
		} else {
			if (needToDecrypt(resultObject)) {
				decryptUtil.decrypt(resultObject);
			}
		}
		return resultObject;
	}
    //判断是否需要解密
	private boolean needToDecrypt(Object object) {
		Class<?> objectClass = object.getClass();
		SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
		return Objects.nonNull(sensitiveData);
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
	}
}

@Intercepts:标识该类是个拦截器

@Signature:标识要拦截那个接口,mybatis可以扩展的拦截器

type :标识具体要拦截那个接口 (ResultSetHandler.class,)

method:标识具体要拦截接口中那个方法(handleResultSets)

args:标识具体要拦截接口方法的入参

使用配置类注入自定义mybatis拦截器

@Configuration
public class IbatisInterceptorConfig{
	
	@Value(value = "${encrypt.enable}")
	public boolean isEnableEncrypt;
	
	@Value(value = "${decrypt.enable}")
	public boolean isEnableDecrypt;
	
	@Autowired
	EncryptProcessor  encryptProcessor;
	
	@Autowired
    DecryptProcessor  decryptProcessor;
	
	@Autowired
	ProcessEngineConfigurationImpl  processEngineFactoryBean;
	
	@PostConstruct
	public void addInterceptor() {
	    //获取项目中的mybatis的SqlSessionFactory
		SqlSessionFactory sqlSessionFactory=processEngineFactoryBean.getSqlSessionFactory();
		
		if(isEnableEncrypt) {
			sqlSessionFactory.getConfiguration().addInterceptor(new EncryptInterceptor(encryptProcessor));
		}
		if(isEnableDecrypt) {
			sqlSessionFactory.getConfiguration().addInterceptor(new DecryptInterceptor(decryptProcessor));
		}
	}
}

配置文件:

application.properties中配置如下

encrypt.enable=true
encrypt.algorithmType=AES
decrypt.enable=true
decrypt.algorithmType=AES

算法统一处理工具

package com.gisquest.platform.common.utils;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import com.gisquest.platform.common.config.Global;

public class CommonAlgorithmUtils {
	
	public static String encrypt(String algorithm,String content){
		String result=StringUtils.EMPTY;
		if(StringUtils.isEmpty(algorithm)) {
			result=content;
			return result;
		}
		if(!StringUtils.isEmpty(algorithm)) {
		  switch(algorithm){
		       case   "AES":
			       result = AESUtils.encrypt(content, Global.getConfig("login.encryptKey"));
		           break;
		       case   "RSA":
		    	   //使用时具体自己补充
		    	   PublicKey publicKey=null;
		    	   Cipher cipher=null;
				try {
					result=RsaUtils.encrypt(content, publicKey, cipher);
					 break;
				} catch (InvalidKeyException | UnsupportedEncodingException | BadPaddingException
						| IllegalBlockSizeException e) {
					e.printStackTrace();
				}
		       case   "SM4":
		    	   result=SM4Utils.encryptEcb(content, SM4Utils.SECURE_LOG_KEY); 
		    	   break;
		       default:
		    	   result = AESUtils.encrypt(content, Global.getConfig("login.encryptKey"));
			}
		}
		return result;
	}
	
	
	
	
	public static String decrypt(String algorithm,String content){
		String result=StringUtils.EMPTY;
		if(StringUtils.isEmpty(algorithm)) {
			result=content;
			return result;
		}
		if(!StringUtils.isEmpty(algorithm)) {
		  switch(algorithm){
		       case   "AES":
			       result = AESUtils.decrypt(content, Global.getConfig("login.encryptKey"));
		           break;
		       case   "RSA":
		    	   //使用时具体自己补充
		    	    PrivateKey privateKey=null;
					try {
						result=RsaUtils.decrypt(content, privateKey);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					 break;
		       case   "SM4":
		    	   //使用时具体自己补充
		    	   result=SM4Utils.decryptEcb(content, SM4Utils.SECURE_LOG_KEY); 
		    	   break;
		       default:
		    	   result = AESUtils.decrypt(content, Global.getConfig("login.encryptKey"));
			}
		}
		return result;
	}
}

import com.gisquest.platform.annotation.SensitiveData;
import com.gisquest.platform.annotation.SensitiveField;

@SensitiveData
public class UserEntity  {
	private String id; 
	private String username;
	@SensitiveField
	private String staffName; // 用户姓名
	//省略setter getter
}
若依框架添加出入参加密解密 若依出入参加密解密,若依全局过滤器使用,byte数组转string丢失数据问题。 阅读详情

相关推荐

Java解析微信获取手机号信息

本文简要介绍了如何接收并解密从微信小程序传递过来的加密手机号信息的方法示例。

m0_72958694的博客 1273

Ceedling深度配置教程:YAML配置文件完全解析与最佳实践

Ceedling是一款专为C项目打造的单元测试和构建系统,通过YAML配置文件可以灵活定制项目的构建流程、测试策略和工具链。本文将深入解析Ceedling的核心配置文件结构,提供实用的配置示例和最佳实践,帮助开发者快速掌握高效配置技巧。 ## 一、Ceedling配置文件基础结构 Ceedling项目的核心配置文件是`project.yml`,位于项目根目录。该文件采用YAML格式,主要包含项

gitblog_00904的博客 465

java纯数字加密解密实例

我们都知道,在用户添加信息时,一些比较敏感的信息,如身份证号,手机号,用户的登录密码等信息,是不能直接明文存进数据库的.今天我们就以一个具体的例子来说明一下纯数字的java加密解密技术.           一般我们从页面获取到用户添加的信息之后,进行加密然后存入到数据库.需要比对信息时,加密之后的用户信息我们看不懂,所以对应的我们就要用解密技术.其实软考中对加密解密技术进行了很全面的说

不忘初心 1万+

Java实现手机号码加密和解密的代码示例

实现使用 Base64 编码,增加了一些简单的混淆,例如添加了一个前缀来识别加密的数据。请注意,此示例并不是非常安全,仅作为示例。在实际使用中,请根据您的需求选择安全性更高的加密算法和密钥管理策略。// 替换成你自己的秘钥。如果手机号码已经被加密,那么调用。要加密您的手机号码,只需调用。

2509

使用springboot+mybatis拦截器实现身份证等生产敏感数据的加解密

在实际生产项目中,经常需要对如身份证信息、手机号、真实姓名等的敏感数据进行加密数据库存储,但在业务代码中对敏感信息进行手动加解密则十分不优雅,甚至会存在错加密、漏加密、业务人员需要知道实际的加密规则等的情况。本文将介绍使用springboot+mybatis拦截器+自定义注解的形式对敏感数据进行存储前拦截加密的详细过程。 目录 一、什么是Mybatis Plugin 二、实现基于注解的敏...

TanzJ的博客 7263

Mysql使用aes加密解密

mysql数据库实现aes加密和解密前言解决过程 前言 这几天碰到一个需求: 需要将mysql里面的敏感数据(手机号码、住址、身份证号码等信息)加密存储。问题是当前数据库里面已经有一定量的数据存在。我需要将数据库里面的数据重新加密。我采用的是aes加密。 写个博客记录一下处理过程,以便之后查看。如有 错误,请各位指正!! 解决过程 在这里我就只拿其中一张表来说了。 表结构如下 : 这里主要要加...

weixin_43623880的博客 5096

使用hutool快速进行身份证手机号脱敏、获取随机数等

Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类,这里演示两个常用工具类,当然他的功能远不止这些。

gl_xxx的博客 2737

Masuit.Tools终极字符串验证指南:Email、手机号身份证号一键校验

在C#开发中,数据验证是保证应用质量的关键环节。Masuit.Tools作为全龄段友好的C#万能工具库,提供了强大的字符串验证功能,让Email、手机号、IP地址、统一社会信用代码等常见数据的校验变得简单高效!🚀 ## 📋 为什么选择Masuit.Tools验证功能? Masuit.Tools的验证功能基于.NET的`ValidationAttribute`体系构建,完美集成到ASP.NE

gitblog_00149的博客 1046

Hutool 实现敏感信息展示脱敏及其反脱敏

业务中如果出现敏感数据展示,数据脱敏是一种重要的安全措施。数据脱敏是指在处理和存储敏感信息时,将这些信息中的敏感部分替换为无意义的数据,以保护用户的隐私和数据安全。本文基于Hutool工具类来实现一键式快速数据脱敏操作

weixin_73077810的博客 4325

敏感加密信息进行模糊查询

有一个问题不知道大家想过没?敏感字段数据是加密存储在数据库的表中,如果需要对这些敏感字段进行模模糊查询,还用原来的通过sql的where从句的like来模糊查询的方式肯定是不行的,那么应该怎么实现呢?这篇文章就来解决这个问题。

qq_45914687的博客 1981

数据脱敏处理

场景 : 数据存储到数据库时 , 除了密码 , 支付密码等敏感数据使用MD5等不可逆的方式加密外 , 其他的一些数据也需用到脱敏处理 , 如手机号 , 身份证号 , 银行卡号等 , 主要原因是当前登录用户或权限用户可能会用到此数据作为前端显示 , 此时需要使用对称加密的方式加密并解密 . 1.方式一 直接使用AES加密 , 解密 , 缺点是查询时需要遍历解密后传给前端 2.方式二 注解的方式加密解密 , 直接操作实体类 1.AES加密代码 public class AESUtil { public sta

LongoingCHU的博客 5212

Spring Boot实战:如何应对敏感字段的模糊查询需求?

这种方法的优点就是原理简单,实现起来也不复杂,但是有一定的局限性,算是一个对性能、业务相折中的一个方案,相比较之下,在能想的方法中,比较推荐这种方法,但是要特别注意的是,对模糊查询的关键字的长度,要在业务层面进行限制;这种方法的优点是,成本比较小,容易实现,但是缺点很明显,该字段无法通过数据库索引来优化查询,另外有一些数据库无法保证数据库的加解密算法与程序的加解密算法一致,可能会导致可以程序中加密,但是无法在数据库中解密的或者可以在数据库加密无法在程序中解密的问题,因此不推荐在生产中使用这种方法;

竹林幽深 566

Hutool:一行代码搞定数据脱敏

在项目的pom.xml的dependencies中加入以下内容,这里以5.8.16版本为例。注意:Hutool 5.x支持JDK8+, 如果你的项目使用JDK7,请使用Hutool 4.x版本。本文使用的数据脱敏工具类只有在5.6+版本以上才提供。本文主要介绍了数据脱敏的相关内容,首先介绍了数据脱敏的概念,在此基础上介绍了常用的数据脱敏规则;

♥回眸相視笑♥的博客 1219

MD5加密数据匹配与破解原理详解,手机号md5解密

本文介绍的综合技术方案为开发者提供了统一、安全、高效的个人信息MD5处理解决方案。通过统一的API接口和丰富的SDK支持,可以方便地集成到各种业务系统中,在保护用户隐私的同时满足业务需求。建议在实际应用中根据具体场景选择合适的数据处理策略和安全措施。

xxl-yydddd 2430

Mybatis插件+注解实现数据脱敏

点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 Elast...

weixin_44421461的博客 520

FastAPI应用加密解密实战:从哈希、AES到RSA签名

在现代Web应用开发中,数据安全是核心议题。加密技术通过可逆的算法(如AES、RSA)和密钥保护敏感信息,确保数据在存储和传输中的机密性与完整性,而哈希算法(如bcrypt)则通过单向函数为密码等凭证提供不可逆的保护。理解对称加密、非对称加密与哈希的原理差异,是构建安全架构的基础。其技术价值在于为系统建立主动防御层,防止数据泄露与篡改,满足合规要求。应用场景广泛,涵盖用户密码存储、敏感信息加密、API通信安全及数字签名验证。本文聚焦于FastAPI框架,系统讲解如何利用passlib进行密码哈希,使用cry

weixin_27875131的博客 262

C# RSA加密解密实战:中文处理与生产环境部署指南

非对称加密是现代数据安全的核心技术之一,它通过公钥加密、私钥解密的机制,解决了密钥分发和管理的难题。其原理基于大数分解等数学难题,确保了即使公钥公开,没有私钥也无法破解密文。这项技术在数据传输安全、身份认证和数字签名等领域具有重要价值,尤其适用于保护敏感信息(如用户身份证号手机号)的传输与存储。在实际工程实践中,开发者常面临中文乱码、跨平台兼容性等挑战。本文聚焦于C#中的RSA实现,深入探讨了如何通过统一使用UTF-8编码、正确处理Base64格式以及采用OAEP安全填充模式,来确保中文字符的加解密可靠性

weixin_28716443的博客 235

uniapp 微信小程序登录与手机号获取全流程解析及常见问题解决

本文详细解析了使用uniapp开发微信小程序时,实现用户登录与获取手机号的全流程。重点阐述了如何通过uni.login获取openid,以及新版API下获取手机号的简化步骤,并深入剖析了常见的javax.crypto.BadPaddingException解密错误的根源与解决方案,帮助开发者高效避坑。

weixin_29252171的博客 93
上一篇: 使用generatorMapper.xml配置遇到的坑
下一篇: 如何在maven官网下载jar
喜羊羊love红太狼
博客等级 码龄9年 487粉丝 268原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值