当前位置: 首页 > news >正文

做古玩生意哪些网站好开源cms建站系统

做古玩生意哪些网站好,开源cms建站系统,微信小程序开通要钱吗,怎么找到某个wordpress背景 我们把数据存到数据库的时候,有些敏感字段是需要加密的,从数据库查出来再进行解密。如果存在多张表或者多个地方需要对部分字段进行加解密操作,每个地方都手写一次加解密的动作,显然不是最好的选择。如果我们使用的是Mybati…

背景

我们把数据存到数据库的时候,有些敏感字段是需要加密的,从数据库查出来再进行解密。如果存在多张表或者多个地方需要对部分字段进行加解密操作,每个地方都手写一次加解密的动作,显然不是最好的选择。如果我们使用的是Mybatis框架,那就跟着一起探索下如何使用框架的拦截器功能实现自动加解密吧。

定义一个自定义注解

我们需要一个注解,只要实体类的属性加上这个注解,那么就对这个属性进行自动加解密。我们把这个注解定义灵活一点,不仅可以放在属性上,还可以放到类上,如果在类上使用这个注解,代表这个类的所有属性都进行自动加密。

/*** 加密字段*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
public @interface EncryptField {}

定义实体类

package com.wen3.demo.mybatisplus.po;import com.baomidou.mybatisplus.annotation.*;
import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;@EncryptField
@Getter
@Setter
@Accessors(chain = true)
@KeySequence(value = "t_user_user_id_seq", dbType = DbType.POSTGRE_SQL)
@TableName("t_USER")
public class UserPo {/*** 用户id*/@TableId(value = "USER_ID", type = IdType.INPUT)private Long userId;/*** 用户姓名*/@TableField("USER_NAME")private String userName;/*** 用户性别*/@TableField("USER_SEX")private String userSex;/*** 用户邮箱*/@EncryptField@TableField("USER_EMAIL")private String userEmail;/*** 用户账号*/@TableField("USER_ACCOUNT")private String userAccount;/*** 用户地址*/@TableField("USER_ADDRESS")private String userAddress;/*** 用户密码*/@TableField("USER_PASSWORD")private String userPassword;/*** 用户城市*/@TableField("USER_CITY")private String userCity;/*** 用户状态*/@TableField("USER_STATUS")private String userStatus;/*** 用户区县*/@TableField("USER_SEAT")private String userSeat;
}

拦截器

Mybatis-Plus有个拦截器接口com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor,但发现这个接口有一些不足

  • 必须构建一个MybatisPlusInterceptor这样的Bean
  • 并调用这个BeanaddInnerInterceptor方法,把所有的InnerInterceptor加入进去,才能生效
  • InnerInterceptor只有before拦截,缺省after拦截。加密可以在before里面完成,但解密需要在after里面完成,所以这个InnerInterceptor不能满足我们的要求

所以继续研究源码,发现Mybatis有个org.apache.ibatis.plugin.Interceptor接口,这个接口能满足我对自动加解密的所有诉求

  • 首先,实现Interceptor接口,只要注册成为Spring容器的Bean,拦截器就能生效
  • 可以更加灵活的在beforeafter之间插入自己的逻辑

加密拦截器

创建名为EncryptInterceptor的加密拦截器,对update操作进行拦截,对带@EncryptField注解的字段进行加密处理,无论是save方法还是saveBatch方法都会被成功拦截到。

package com.wen3.demo.mybatisplus.encrypt.interceptor;import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import com.wen3.demo.mybatisplus.encrypt.util.FieldEncryptUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Objects;/*** 对update操作进行拦截,对{@link EncryptField}字段进行加密处理;* 无论是save方法还是saveBatch方法都会被成功拦截;*/
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Component
public class EncryptInterceptor implements Interceptor {private static final String METHOD = "update";@Setter(onMethod_ = {@Autowired})private FieldEncryptUtil fieldEncryptUtil;@Overridepublic Object intercept(Invocation invocation) throws Throwable {if(!StringUtils.equals(METHOD, invocation.getMethod().getName())) {return invocation.proceed();}// 根据update拦截规则,第0个参数一定是MappedStatement,第1个参数是需要进行判断的参数Object param = invocation.getArgs()[1];if(Objects.isNull(param)) {return invocation.proceed();}// 加密处理fieldEncryptUtil.encrypt(param);return invocation.proceed();}
}

解密拦截器

创建名为DecryptInterceptor的加密拦截器,对query操作进行拦截,对带@EncryptField注解的字段进行解密处理,无论是返回单个对象,还是对象的集合,都会被拦截到。

package com.wen3.demo.mybatisplus.encrypt.interceptor;import cn.hutool.core.util.ClassUtil;
import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import com.wen3.demo.mybatisplus.encrypt.util.FieldEncryptUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.sql.Statement;
import java.util.Collection;/*** 对query操作进行拦截,对{@link EncryptField}字段进行解密处理;*/
@Slf4j
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class)
})
@Component
public class DecryptInterceptor implements Interceptor {private static final String METHOD = "query";@Setter(onMethod_ = {@Autowired})private FieldEncryptUtil fieldEncryptUtil;@SuppressWarnings("rawtypes")@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object result = invocation.proceed();// 解密处理// 经过测试发现,无论是返回单个对象还是集合,result都是ArrayList类型if(ClassUtil.isAssignable(Collection.class, result.getClass())) {fieldEncryptUtil.decrypt((Collection) result);} else {fieldEncryptUtil.decrypt(result);}return result;}
}

加解密工具类

由于加密和解密绝大部分的逻辑是相似的,不同的地方在于

  • 加密需要通过反射处理的对象,是在SQL执行前,是Invocation对象的参数列表中下标为1的参数;而解决需要通过反射处理的对象,是在SQL执行后,对执行结果对象进行解密处理。
  • 一个是获取到字段值进行加密,一个是获取到字段值进行解密

于是把加解密逻辑抽象成一个工具类,把差异的部分做为参数传入

package com.wen3.demo.mybatisplus.encrypt.util;import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import com.wen3.demo.mybatisplus.encrypt.service.FieldEncryptService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Objects;/*** 加解密工具类*/
@Slf4j
@Component
public class FieldEncryptUtil {@Setter(onMethod_ = {@Autowired})private FieldEncryptService fieldEncryptService;/**对EncryptField注解进行加密处理*/public void encrypt(Object obj) {if(ClassUtil.isPrimitiveWrapper(obj.getClass())) {return;}encryptOrDecrypt(obj, true);}/**对EncryptField注解进行解密处理*/public void decrypt(Object obj) {encryptOrDecrypt(obj, false);}/**对EncryptField注解进行解密处理*/public void decrypt(Collection list) {if(CollectionUtils.isEmpty(list)) {return;}list.forEach(this::decrypt);}/**对EncryptField注解进行加解密处理*/private void encryptOrDecrypt(Object obj, boolean encrypt) {// 根据update拦截规则,第0个参数一定是MappedStatement,第1个参数是需要进行判断的参数if(Objects.isNull(obj)) {return;}// 获取所有带加密注解的字段List<Field> encryptFields = null;// 判断类上面是否有加密注解EncryptField encryptField = AnnotationUtils.findAnnotation(obj.getClass(), EncryptField.class);if(Objects.nonNull(encryptField)) {// 如果类上有加密注解,则所有字段都需要加密encryptFields = FieldUtils.getAllFieldsList(obj.getClass());} else {encryptFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), EncryptField.class);}// 没有字段需要加密,则跳过if(CollectionUtils.isEmpty(encryptFields)) {return;}encryptFields.forEach(f->{// 只支持String类型的加密if(!ClassUtil.isAssignable(String.class, f.getType())) {return;}String oldValue = (String) ReflectUtil.getFieldValue(obj, f);if(StringUtils.isBlank(oldValue)) {return;}String logText = null, newValue = null;if(encrypt) {logText = "encrypt";newValue = fieldEncryptService.encrypt(oldValue);} else {logText = "decrypt";newValue = fieldEncryptService.decrypt(oldValue);}log.info("{} success[{}=>{}]. before:{}, after:{}", logText, f.getDeclaringClass().getName(), f.getName(), oldValue, newValue);ReflectUtil.setFieldValue(obj, f, newValue);});}
}

加解密算法

Mybatis-Plus自带了一个AES加解密算法的工具,我们只需要提供一个加密key,然后就可以完成一个加解密的业务处理了。

  • 先定义一个加解密接口
package com.wen3.demo.mybatisplus.encrypt.service;/*** 数据加解密接口*/
public interface FieldEncryptService {/**对数据进行加密*/String encrypt(String value);/**对数据进行解密*/String decrypt(String value);/**判断数据是否忆加密*/default boolean isEncrypt(String value) {return false;}
}
  • 然后实现一个默认的加解密实现类
package com.wen3.demo.mybatisplus.encrypt.service.impl;import cn.hutool.core.util.ClassUtil;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.AES;
import com.wen3.demo.mybatisplus.encrypt.service.FieldEncryptService;
import org.springframework.stereotype.Component;import javax.crypto.IllegalBlockSizeException;/*** 使用Mybatis-Plus自带的AES加解密*/
@Component
public class DefaultFieldEncryptService implements FieldEncryptService {private static final String ENCRYPT_KEY = "abcdefghijklmnop";@Overridepublic String encrypt(String value) {if(isEncrypt(value)) {return value;}return AES.encrypt(value, ENCRYPT_KEY);}@Overridepublic String decrypt(String value) {return AES.decrypt(value, ENCRYPT_KEY);}@Overridepublic boolean isEncrypt(String value) {// 判断是否已加密try {// 解密成功,说明已加密decrypt(value);return true;} catch (MybatisPlusException e) {if(ClassUtil.isAssignable(IllegalBlockSizeException.class, e.getCause().getClass())) {return false;}throw e;}}
}

自动加解密单元测试

package com.wen3.demo.mybatisplus.service;import cn.hutool.core.util.RandomUtil;
import com.wen3.demo.mybatisplus.MybatisPlusSpringbootTestBase;
import com.wen3.demo.mybatisplus.encrypt.service.FieldEncryptService;
import com.wen3.demo.mybatisplus.po.UserPo;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Test;import java.util.Collections;
import java.util.List;
import java.util.Map;class UserServiceTest extends MybatisPlusSpringbootTestBase {@Resourceprivate UserService userService;@Resourceprivate FieldEncryptService fieldEncryptService;@Testvoid save() {UserPo userPo = new UserPo();String originalValue = RandomStringUtils.randomAlphabetic(16);String encryptValue = fieldEncryptService.encrypt(originalValue);userPo.setUserEmail(originalValue);userPo.setUserName(RandomStringUtils.randomAlphabetic(16));boolean testResult = userService.save(userPo);assertTrue(testResult);assertNotEquals(originalValue, userPo.getUserEmail());assertEquals(encryptValue, userPo.getUserEmail());// 测试解密: 返回单个对象UserPo userPoQuery = userService.getById(userPo.getUserId());assertEquals(originalValue, userPoQuery.getUserEmail());// 测试解密: 返回ListList<UserPo> userPoList = userService.listByEmail(encryptValue);assertEquals(originalValue, userPoList.get(0).getUserEmail());// 测试saveBatch方法也会被拦截加密userPo.setUserId(null);testResult = userService.save(Collections.singletonList(userPo));assertTrue(testResult);assertNotEquals(originalValue, userPo.getUserEmail());assertEquals(encryptValue, userPo.getUserEmail());}
}

单元测试运行截图

在这里插入图片描述

http://www.yidumall.com/news/48652.html

相关文章:

  • WordPress主题改为html模板搜索引擎优化面对哪些困境
  • 安阳网站建设哪家专业刷评论网站推广
  • 网站建设经营范围国外免费建站网站
  • 公司网站二维码怎么做哪里有永久免费建站
  • 苏州市建设局招标网站百度快照优化排名推广怎么做
  • 合肥做淘宝网站建设最新足球消息
  • 定兴网站建设长春关键词优化报价
  • 影视传媒网站源码seo包年服务
  • 昆明网站搭建百度资源站长平台
  • 国外做蒸汽锅炉的网站推广代理公司
  • 易动力建设网站怎么样站长之家素材
  • 廊坊网站排名优化公司哪家好上海网络推广团队
  • zencart网站管理目前主流搜索引擎是哪种
  • 石家庄做网站公司全媒体运营师报名入口
  • 柳州公积金网站深圳网络推广网站推广
  • 大兴网站建设优化seoseo课程培训中心
  • 微信公众号做公司网站今日最新国内新闻
  • 个体可以做企业网站吗营销策略都有哪些方面
  • 濮阳建站公司流程优化网站seo
  • 做电影网站 广告收入网络营销推广的
  • 网站建设支付国内新闻摘抄2022年
  • 股票网站排名哪个好良品铺子网络营销策划书
  • 做网站不知道做什么内容的长春网站制作企业
  • 传奇网页游戏变态版seoul national university
  • 网站开发设计师广州网站到首页排名
  • 建设公司和建筑公司有什么区别搜索引擎优化是做什么
  • 淘宝店可以做团购的网站sem竞价专员
  • 郑州做网站公司有多少钱百度快速排名系统查询
  • 如何在360网站网页上做笔记安徽网络建站
  • 做网站临沂谷歌seo排名