實作:
- 自定義注解類
- 自定義myabtis攔截器,攔截mybatis,主要涉及三個handler(StatementHandler,ParameterHandler,ResultSetHandler)
- 自定義加解密工具類
- 自定義業務處理Service(根據業務自行開發)
- 自定義注解添加再物體類及需要加解密欄位上進行簡單增改查測驗
- 1. 自定義注解類
import java.lang.annotation.*; /** * ===================================== * *******開發部 * ===================================== * * @author 開發者 * @version 1.0-SNAPSHOT * * @date 2023/2/6 */ @Target({ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface SensitiveEntity { }
import java.lang.annotation.*;
/**
* =====================================
* ***********開發部
* =====================================
*
* @author 開發者
* @version 1.0-SNAPSHOT
*
* @date 2023/2/6
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SensitiveField {
}
- 2. 自定義攔截器
import ********.SensitiveEntity; import ********.AesService; import lombok.extern.slf4j.Slf4j; 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.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 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; /** * ===================================== * ***********開發部 * ===================================== * * @version 1.0-SNAPSHOT * @description * @date 2023/2/10 */ @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 { @Resource private AesService aesService; @Override public 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; } //基于selectList if (resultObject instanceof ArrayList) { ArrayList resultList = (ArrayList) resultObject; if (CollectionUtils.isEmpty(resultList) || !needToDecrypt(resultList.get(0))) { return resultObject; } for (Object result : resultList) { //逐一解密 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 ,這里則能強轉為ResultSetHandler ParameterHandler 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所注解 SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveEntity.class); //未被@SensitiveEntity所注解 則為null if (Objects.isNull(sensitiveEntity)) { return invocation.proceed(); } //取出當前當前類所有欄位,傳入加密方法 Field[] declaredFields = parameterObjectClass.getDeclaredFields(); aesService.encrypt(declaredFields, 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(); sql = aesService.replaceAll(sql); 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) { Class<?> objectClass = object.getClass(); SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(objectClass, SensitiveEntity.class); return Objects.nonNull(sensitiveEntity); } @Override public Object plugin(Object target) { return Interceptor.super.plugin(target); } @Override public void setProperties(Properties properties) { Interceptor.super.setProperties(properties); } }
- 3. 自定義解密工具類
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; /** * ===================================== * ****************開發部 * ===================================== * * @author 開發者 * @version 1.0-SNAPSHOT * @description * @date 2023/2/10 */ @Component @Slf4j 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 final String BASE64_RULE = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"; /** * 正則驗證物件 */ private final Pattern PATTERN = Pattern.compile(BASE64_RULE); /** * 加解密 密鑰key */ @Value("${encrypt.key}") public static String encryptKey; /** * @param content 加密字串 * @return 加密結果 */ public String encrypt(String content) { return encrypt(content, encryptKey); } /** * 加密 * * @param content 加密引數 * @param key 加密key * @return 結果字串 */ public String encrypt(String content, String key) { //判斷如果已經是base64加密字串則回傳原字串 if (isBase64(content)) { return content; } byte[] encrypted = encrypt2bytes(content, key); if (null == encrypted || encrypted.length < 1) { log.info("加密字串[{}]轉位元組為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, encryptKey); } 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 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 結果 */ private boolean isBase64(String str) { Matcher matcher = PATTERN.matcher(str); return matcher.matches(); } }
- 4. 自定義業務處理Service
import *****************.SensitiveField; import *****************.AesService; import *****************.AesFieldUtils; import *****************.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.lang.reflect.Field; import java.util.Objects; /** * ===================================== * *****************開發部 * ===================================== * * @author 開發者 * @version 1.0-SNAPSHOT * @description * @date 2023/2/10 */ @Service public class AesServiceImpl implements AesService { @Value("${aes.key}") private String key; @Value("${aes.keyField}") private String keyField; @Resource private AesFieldUtils aesFieldUtils; @Override public <T> T encrypt(Field[] declaredFields, T paramsObject) throws Exception { for (Field field : declaredFields) { //取出所有被EncryptDecryptField注解的欄位 SensitiveField sensitiveField = field.getAnnotation(SensitiveField.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.noEmpty(value)) { continue; } //加密 這里我使用自定義的AES加密工具 field.set(paramsObject, aesFieldUtils.encrypt(value, key)); } } return paramsObject; } @Override public <T> T decrypt(T result) throws Exception { //取出resultType的類 Class<?> resultClass = result.getClass(); Field[] declaredFields = resultClass.getDeclaredFields(); for (Field field : declaredFields) { //取出所有被EncryptDecryptField注解的欄位 SensitiveField sensitiveField = field.getAnnotation(SensitiveField.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.noEmpty(value)) { continue; } //對注解的欄位進行逐一解密 field.set(result, aesFieldUtils.decrypt(value, key)); } } return result; } @Override public String replaceAll(String sql) { if (sql.contains(keyField)) { return sql.replaceAll(keyField, key); } return null; } }
- 5. 測驗代碼
import **************.SensitiveEntity; import **************.SensitiveField; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; @SensitiveEntity @TableName("t_users") public class Users extends Model<Users> { /** * * This field was generated by MyBatis Generator. * This field corresponds to the database column t_users.id * * @mbg.generated Wed Feb 01 10:03:44 CST 2023 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * * This field was generated by MyBatis Generator. * This field corresponds to the database column t_users.user_id * * @mbg.generated Wed Feb 01 10:03:44 CST 2023 */ private String userId; /** * * This field was generated by MyBatis Generator. * This field corresponds to the database column t_users.user_name * * @mbg.generated Wed Feb 01 10:03:44 CST 2023 */ private String userName; /** * * This field was generated by MyBatis Generator. * This field corresponds to the database column t_users.nick_name * * @mbg.generated Wed Feb 01 10:03:44 CST 2023 */ private String nickName; /** * * This field was generated by MyBatis Generator. * This field corresponds to the database column t_users.password * * @mbg.generated Wed Feb 01 10:03:44 CST 2023 */ @SensitiveField private String password; /** * * This field was generated by MyBatis Generator. * This field corresponds to the database column t_users.pwd_duration * * @mbg.generated Wed Feb 01 10:03:44 CST 2023 */ private String pwdDuration; @SensitiveField private String birth;
}
@Test
public void add(){
Users user = new Users();
user.setUserId("test03");
user.setUserName("小明004");
user.setNickName("test03");
user.setPassword("12343454123");
user.setPwdDuration("124124124214");
usersService.insert(user);
}
@Test
public void update(){
Users user = new Users();
user.setUserId("test03");
user.setUserName("小明03");
user.setNickName("test03");
user.setPassword("1252525125123");
user.setPwdDuration("1252525125121");
usersService.updateByPrimaryKeySelective(user);
}
@Test
public void queryTest(){
System.out.println(JSON.toJSON(usersService.selectByPrimaryKey(3)));
}
本文原創:如有使用請標明出處
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543730.html
標籤:其他
