mybatis特定字段注解加解密(SM2加密)

编程入门 行业动态 更新时间:2024-10-09 16:29:48

mybatis特定字段<a href=https://www.elefans.com/category/jswz/34/1768912.html style=注解加解密(SM2加密)"/>

mybatis特定字段注解加解密(SM2加密)

1、创建特定字段注解

字段注解

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

类注解 

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

2、创建加密方法

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.HashMap;
import java.util.Map;public class SM2Utils {/*** SM2加密算法** @param publicKey 公钥* @param data      明文数据* @return*/public static String encrypt(String publicKey, String data) {// 获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数NECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(),sm2ECParameters.getN());//提取公钥点ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);// 设置sm2为加密模式sm2Engine.init(true,new ParametersWithRandom(publicKeyParameters, new SecureRandom()));byte[] arrayOfBytes = null;try {byte[] in = data.getBytes();arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);} catch (Exception e) {System.out.println("SM2加密时出现异常:" + e.getMessage());}return Hex.toHexString(arrayOfBytes);}/*** SM2解密算法** @param privateKey 私钥* @param cipherData 密文数据* @return*/public static String decrypt(String privateKey, String cipherData) {// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上if (!cipherData.startsWith("04")) {cipherData = "04" + cipherData;}byte[] cipherDataByte = Hex.decode(cipherData);BigInteger privateKeyD = new BigInteger(privateKey, 16);//获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(),sm2ECParameters.getN());ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);// 设置sm2为解密模式sm2Engine.init(false, privateKeyParameters);String result = "";try {byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0,cipherDataByte.length);return new String(arrayOfBytes);} catch (Exception e) {System.out.println("SM2解密时出现异常:" + e.getMessage());}return result;}//生成密钥public static Map<String, String> getKey() throws Exception {ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");// 获取一个椭圆曲线类型的密钥对生成器KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());// 使用SM2参数初始化生成器kpg.initialize(sm2Spec);// 获取密钥对KeyPair keyPair = kpg.generateKeyPair();PublicKey publicKey = keyPair.getPublic();BCECPublicKey p = (BCECPublicKey) publicKey;PrivateKey privateKey = keyPair.getPrivate();BCECPrivateKey s = (BCECPrivateKey) privateKey;Map<String, String> key = new HashMap<>();key.put("publicKey", Hex.toHexString(p.getQ().getEncoded(false)));key.put("privateKey", Hex.toHexString(s.getD().toByteArray()));return key;}public static void main(String[] args) throws Exception {String publicKey = "042b62ad17c0a3191bc46df6b9d93eaddc64809755a913dcb348da2c1d186f2523077cff9fa52fe9e0da2d3d4f01307397f680589";String privateKey = "0098dd20430be7810c618201010ec6fc32868a9afd9da8";String phone = "1358273";String encryptRes = encrypt(publicKey, phone);System.out.println("手机号" + phone + "加密:" + encryptRes);System.out.println("手机号" + phone + "解密:" + decrypt(privateKey, encryptRes));}
}

3、创建mybatis拦截器


import com.xinjianmon.encrypt.EncryptField;
import com.xinjianmon.encrypt.SM2Utils;
import com.xinjianmon.encrypt.SensitiveClass;
import com.xinjianmon.utils.StringUtils;
import org.apachemons.collections4.CollectionUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
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.Signature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
public class MybatisInterceptor implements Interceptor {@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);}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) || !needToDecrypt(resultList.get(0))) {return resultObject;}for (Object result : resultList) {//逐一解密decrypt(result);}//基于selectOne} else {if (needToDecrypt(resultObject)) {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 (null == parameterObject) {return invocation.proceed();}Class<?> parameterObjectClass = parameterObject.getClass();//校验该实例的类是否被@SensitiveEntity所注解SensitiveClass sensitiveEntity = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveClass.class);//未被@SensitiveEntity所注解 则为nullif (Objects.isNull(sensitiveEntity)) {return invocation.proceed();}//取出当前当前类所有字段,传入加密方法Field[] declaredFields = parameterObjectClass.getDeclaredFields();encrypt(declaredFields, parameterObject);return invocation.proceed();}public Object encrypt(Field[] declaredFields, Object paramsObject) throws Exception {String publicKey = "042b62ad17c0a3191bceaddc64809755a913dcb348da2c1d186f2523077cff9fa52fe9e0da2d3d4f01307397f680589";for (Field field : declaredFields) {//取出所有被EncryptDecryptField注解的字段EncryptField sensitiveField = field.getAnnotation(EncryptField.class);if (Objects.isNull(sensitiveField)) {continue;}field.setAccessible(true);Object object = field.get(paramsObject);//暂时只实现String类型的加密if (object instanceof String) {String value = (String) object;//如果映射字段值为空,并且以==结尾则跳过不进行加密if (!StringUtils.isNotBlank(value)) {continue;}//加密field.set(paramsObject, SM2Utils.encrypt(publicKey, value));}}return paramsObject;}public Object decrypt(Object result) throws Exception {String privateKey = "0098dd20430be7818e3e64430c6ec6fc32868a9afd9da8";//取出resultType的类Class<?> resultClass = result.getClass();Field[] declaredFields = resultClass.getDeclaredFields();for (Field field : declaredFields) {//取出所有被EncryptDecryptField注解的字段EncryptField sensitiveField = field.getAnnotation(EncryptField.class);if (Objects.isNull(sensitiveField)) {continue;}field.setAccessible(true);Object object = field.get(result);//只支持String的解密if (object instanceof String) {String value = (String) object;//如果映射字段值为空,并且不已==结尾则跳过不进行解密if (!StringUtils.isNotBlank(value)) {continue;}//对注解的字段进行逐一解密field.set(result, SM2Utils.decrypt(privateKey, value));}}return result;}/*** 判断是否包含需要加解密对象** @param object 参数* @return 结果*/private boolean needToDecrypt(Object object) {Class<?> objectClass = object.getClass();SensitiveClass sensitiveEntity = AnnotationUtils.findAnnotation(objectClass, SensitiveClass.class);return Objects.nonNull(sensitiveEntity);}@Overridepublic Object plugin(Object target) {return Interceptor.super.plugin(target);}@Overridepublic void setProperties(Properties properties) {Interceptor.super.setProperties(properties);}}

4、MyBatisConfig配置拦截器

import com.xinjianmon.utils.StringUtils;
import com.xinjian.framework.interceptor.MybatisInterceptor;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;/*** Mybatis支持*匹配扫描包** @author xinjian*/
@Configuration
public class MyBatisConfig {static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";@Autowiredprivate Environment env;@Autowiredprivate MybatisInterceptor mybatisInterceptor;public static String setTypeAliasesPackage(String typeAliasesPackage) {ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);List<String> allResult = new ArrayList<String>();try {for (String aliasesPackage : typeAliasesPackage.split(",")) {List<String> result = new ArrayList<String>();aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;Resource[] resources = resolver.getResources(aliasesPackage);if (resources != null && resources.length > 0) {MetadataReader metadataReader = null;for (Resource resource : resources) {if (resource.isReadable()) {metadataReader = metadataReaderFactory.getMetadataReader(resource);try {result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());} catch (ClassNotFoundException e) {e.printStackTrace();}}}}if (result.size() > 0) {HashSet<String> hashResult = new HashSet<String>(result);allResult.addAll(hashResult);}}if (allResult.size() > 0) {typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));} else {throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");}} catch (IOException e) {e.printStackTrace();}return typeAliasesPackage;}public Resource[] resolveMapperLocations(String[] mapperLocations) {ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();List<Resource> resources = new ArrayList<Resource>();if (mapperLocations != null) {for (String mapperLocation : mapperLocations) {try {Resource[] mappers = resourceResolver.getResources(mapperLocation);resources.addAll(Arrays.asList(mappers));} catch (IOException e) {// ignore}}}return resources.toArray(new Resource[resources.size()]);}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");String mapperLocations = env.getProperty("mybatis.mapperLocations");String configLocation = env.getProperty("mybatis.configLocation");typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);VFS.addImplClass(SpringBootVFS.class);final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);sessionFactory.setTypeAliasesPackage(typeAliasesPackage);sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));sessionFactory.setPlugins(mybatisInterceptor);return sessionFactory.getObject();}
}

5、加解密类添加注解


import com.xinjianmon.annotation.Excel;
import com.xinjianmon.annotation.Excel.ColumnType;
import com.xinjianmon.annotation.Excel.Type;
import com.xinjianmon.annotation.Excels;
import com.xinjianmon.core.domain.BaseEntity;
import com.xinjianmon.encrypt.EncryptField;
import com.xinjianmon.encrypt.SensitiveClass;
import com.xinjianmon.xss.Xss;
import org.apachemons.lang3.builder.ToStringBuilder;
import org.apachemons.lang3.builder.ToStringStyle;import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;/*** 用户对象 sys_user** @author xinjian*/
@SensitiveClass
public class SysUser extends BaseEntity {private static final long serialVersionUID = 1L;/*** 用户ID*/@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")private Long userId;/*** 部门ID*/@Excel(name = "部门编号", type = Type.IMPORT)private Long deptId;/*** 微信用户ID*/private String openid;/*** 用户账号*/@Excel(name = "登录名称")private String userName;/*** 用户昵称*/@Excel(name = "用户名称")private String nickName;/*** 用户邮箱*/@Excel(name = "用户邮箱")private String email;/*** 手机号码*/@Excel(name = "手机号码")@EncryptFieldprivate String phonenumber;/*** 用户性别*/@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")private String sex;/*** 用户头像*/private String avatar;/*** 密码*/private String password;/*** 帐号状态(0正常 1停用)*/@Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")private String status;/*** 删除标志(0代表存在 2代表删除)*/private String delFlag;/*** 最后登录IP*/@Excel(name = "最后登录IP", type = Type.EXPORT)private String loginIp;/*** 最后登录时间*/@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)private Date loginDate;/*** 部门对象*/@Excels({@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),@Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)})private SysDept dept;/*** 角色对象*/private List<SysRole> roles;/*** 角色组*/private Long[] roleIds;/*** 岗位组*/private Long[] postIds;/*** 角色ID*/private Long roleId;public SysUser() {}public SysUser(Long userId) {this.userId = userId;}public static boolean isAdmin(Long userId) {return userId != null && 1L == userId;}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public boolean isAdmin() {return isAdmin(this.userId);}public Long getDeptId() {return deptId;}public void setDeptId(Long deptId) {this.deptId = deptId;}@Xss(message = "用户昵称不能包含脚本字符")@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}@Xss(message = "用户账号不能包含脚本字符")@NotBlank(message = "用户账号不能为空")@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}@Email(message = "邮箱格式不正确")@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")public String getPhonenumber() {return phonenumber;}public void setPhonenumber(String phonenumber) {this.phonenumber = phonenumber;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAvatar() {return avatar;}public void setAvatar(String avatar) {this.avatar = avatar;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public String getDelFlag() {return delFlag;}public void setDelFlag(String delFlag) {this.delFlag = delFlag;}public String getLoginIp() {return loginIp;}public void setLoginIp(String loginIp) {this.loginIp = loginIp;}public Date getLoginDate() {return loginDate;}public void setLoginDate(Date loginDate) {this.loginDate = loginDate;}public SysDept getDept() {return dept;}public void setDept(SysDept dept) {this.dept = dept;}public List<SysRole> getRoles() {return roles;}public void setRoles(List<SysRole> roles) {this.roles = roles;}public Long[] getRoleIds() {return roleIds;}public void setRoleIds(Long[] roleIds) {this.roleIds = roleIds;}public Long[] getPostIds() {return postIds;}public void setPostIds(Long[] postIds) {this.postIds = postIds;}public Long getRoleId() {return roleId;}public void setRoleId(Long roleId) {this.roleId = roleId;}@Overridepublic String toString() {return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("userId", getUserId()).append("deptId", getDeptId()).append("userName", getUserName()).append("nickName", getNickName()).append("email", getEmail()).append("phonenumber", getPhonenumber()).append("sex", getSex()).append("avatar", getAvatar()).append("password", getPassword()).append("status", getStatus()).append("delFlag", getDelFlag()).append("loginIp", getLoginIp()).append("loginDate", getLoginDate()).append("createBy", getCreateBy()).append("createTime", getCreateTime()).append("updateBy", getUpdateBy()).append("updateTime", getUpdateTime()).append("remark", getRemark()).append("dept", getDept()).toString();}
}

6、实现效果

更多推荐

mybatis特定字段注解加解密(SM2加密)

本文发布于:2023-11-15 09:26:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1597408.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:注解   字段   加解密   mybatis

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!