网站建设运维情况,网站seo策划方案,wordpress点击图片上传,微网站开发平台 知乎目录 前言实现步骤定义注解加密工具类定义mybatis拦截器 总结 前言
一些敏感信息存入数据需要进行加密处理#xff0c;比如电话号码#xff0c;身份证号码等#xff0c;从数据库取出到前端展示时需要解密#xff0c;如果分别在存入取出时去做处理#xff0c;会很繁锁比如电话号码身份证号码等从数据库取出到前端展示时需要解密如果分别在存入取出时去做处理会很繁锁至此我查了很多相关资料最后得到一个比较完美的解决方案。
实现步骤
定义注解
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拦截器非常强大它可以对所有存入数据库的数据进行处理包括更新插入查询和删除…,对数据做统一处理非常方便。