mybatis拦截器的使用

场景:自定义拦截器做数据的加解密。

  1. 注册自定义拦截器

    @Configuration
    public class MyBatisConfig {
    
    // TODO: 注册自定义拦截器
    @Bean
    @Order(999)
    public EncryptionInterceptor encryptionInterceptor() {
        return new EncryptionInterceptor();
    }
    }
  2. 使用mybatis拦截器拦截查询。

    @Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class EncryptionInterceptor implements Interceptor {
    
    @Autowired
    private EncryptionConfig encryptionConfig;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
    
        // 判断操作类型是insert, update 或 delete
        if (ms.getSqlCommandType().equals(SqlCommandType.INSERT) ||
                ms.getSqlCommandType().equals(SqlCommandType.UPDATE) ||
                ms.getSqlCommandType().equals(SqlCommandType.DELETE)) {
            // TODO: 加密字段
            encryptFields(parameter);
        } else if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
            // TODO: 查询操作,在执行后需要对结果进行解密
            Object result = invocation.proceed();
            List<EncryptionConfig.Models> models = encryptionConfig.getModels();
            for (EncryptionConfig.Models model : models) {
                if (judgmentModel(parameter, model)) {
                    decryptFields(result);
                }
            }
            return result;
        }
    
        return invocation.proceed();
    }
    
    private Boolean judgmentModel(Object parameter, EncryptionConfig.Models model) {
        MetaObject metaObject = SystemMetaObject.forObject(parameter);
        if (metaObject.getOriginalObject() instanceof MapperMethod.ParamMap) {
            if (metaObject.hasGetter("ew")) {
                Object param1 = metaObject.getValue("ew");
                if (param1 != null) {
                    Object originalObject = SystemMetaObject.forObject(param1).getOriginalObject();
                    if (originalObject instanceof QueryWrapper) {
                        DataMap entity = (DataMap) ((QueryWrapper<?>) originalObject).getEntity();
                        if (entity != null) {
                            Object modelFieldName = entity.get(FieldConstants._d_modelFieldName);
                            if (modelFieldName != null) {
                                String modelName = modelFieldName.toString();
                                return model.getModel().equals(modelName);
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
    
    private void encryptFields(Object parameter) throws Exception {
        List<EncryptionConfig.Models> models = encryptionConfig.getModels();
        MetaObject metaObject = SystemMetaObject.forObject(parameter);
        if (metaObject.getOriginalObject() instanceof MapperMethod.ParamMap) {
            if (metaObject.hasGetter("et")) {
                Object param1 = metaObject.getValue("et");
                // 确保 param1 不为空
                if (param1 != null) {
                    MetaObject param1MetaObject = SystemMetaObject.forObject(param1);
                    if (param1MetaObject.getOriginalObject() instanceof LinkedHashMap) {
                        // 再检查 _d_model 字段
                        if (param1MetaObject.hasGetter(FieldConstants._d_modelFieldName)) {
                            String value1 = (String) param1MetaObject.getValue(FieldConstants._d_modelFieldName);
                            for (EncryptionConfig.Models model : models) {
                                if (model.getModel().equals(value1)) {
                                    for (String field : model.getFields()) {
                                        // 使用反射获取字段值并进行加密
                                        Object value = metaObject.getValue("et." + field);
                                        if (value != null) {
                                            String encryptedValue = EncryptionUtil.encrypt(value.toString());
                                            metaObject.setValue("et." + field, encryptedValue);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    private void decryptFields(Object result) throws Exception {
        List<EncryptionConfig.Models> models = encryptionConfig.getModels();
        if (result instanceof ArrayList) {
            ArrayList<?> resultValue = (ArrayList<?>) result;
            for (EncryptionConfig.Models model : models) {
                for (String field : model.getFields()) {
                    for (Object o : resultValue) {
                        DataMap dataMap = (DataMap) o;
                        Object fieldName = dataMap.get(field);
                        if (fieldName != null) {
                            String encryptedValue = fieldName.toString();
                            String decryptedValue = EncryptionUtil.decrypt(encryptedValue);
                            dataMap.setValue(field, decryptedValue);
                        }
                    }
                }
            }
        }
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
    }
    }
  3. 定义实体类,用于筛选模型

    @Configuration
    @ConfigurationProperties(prefix = "encryption")
    public class EncryptionConfig {
    private List<Models> models;
    
    public static class Models {
        private String model;
        private List<String> fields;
    
        public String getModel() {
            return model;
        }
    
        public void setModel(String model) {
            this.model = model;
        }
    
        public List<String> getFields() {
            return fields;
        }
    
        public void setFields(List<String> fields) {
            this.fields = fields;
        }
    }
    
    public List<Models> getModels() {
        return models;
    }
    
    public void setModels(List<Models> models) {
        this.models = models;
    }
    }

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/dai-ma-shi-jian/19717.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
yexiu's avataryexiu数式员工
Previous 2024年12月1日 pm7:18
Next 2024年12月3日 pm11:51

相关推荐

  • 【前端】项目开发前端知识要点地图

    概述 下面整理了目前现有的所有文章,并提供了基本的学习路径。所有使用*标记的文章属于推荐必读文章。 目录 基础篇 【路由】浏览器地址栏url参数介绍 母版-布局-DSL 渲染基础(v4)* 组件SPI机制(v4)* 组件数据交互基础(v4)* 组件生命周期(v4) 入门篇 自定义视图组件(v4)* 如何通过浏览器开发者工具提高调试效率* 如何提高自定义组件的开发效率* 自定义组件之自动渲染(组件插槽的使用)(v4)* GraphQL请求详解(v4)* 上下文在字段和动作中的应用 如何实现页面间的跳转 如何自定义指定页面的样式 进阶篇 自定义组件之手动渲染基础(v4) 自定义组件之手动渲染弹出层(v4) 自定义组件之手动渲染任意视图(v4) 【前端】IOC容器(v4) 最佳实践篇 【前端】工程结构最佳实践(v4)* 【前端】移动端工程结构最佳实践(v4)* 界面设计器实战篇 基础篇 【界面设计器】模型增删改查基础 【界面设计器】他表字段 【界面设计器】左树右表 【界面设计器】树形表格 【界面设计器】树下拉/级联选择 【界面设计器】自定义字段组件基础 展示篇 【界面设计器】自定义字段组件实战——轮播图 【界面设计器】自定义字段组件实战——表格字段组合展示 【界面设计器】自定义字段组件实战——表格字段内嵌表格 交互篇 【界面设计器】自定义字段组件实战——千分位输入框 其他 前端低无一体使用教程 如何自定义表格字段? 【界面设计器】组件开发常见问题 【前端】低无一体部署常见问题 【前端】生产环境性能调优 API文档 OioProvider详解(v4.3.0)* 前端环境配置(v4)* 默认布局模板(v4) 表格主题配置(v4) 运行时上下文API文档(v4) Class Component(ts)(v4)

    2024年5月25日
    5.5K00
  • 推送自定义消息

    项目中添加消息依赖 pro.shushi.pamirs.core pamirs-message-api 调用pro.shushi.pamirs.message.engine.message.MessageSender#sendSystemMail发送系统消息示例: @Action(displayName = "发送消息") public Student sendMessage(Student data){ MessageSender mailSender = (MessageSender) MessageEngine.get(MessageEngineTypeEnum.MAIL_SEND).get(null); String content = "发送自定义消息"; String subject = null; List<Long> userIds = new ArrayList<>(); userIds.add(PamirsSession.getUserId()); PamirsMessage message = new PamirsMessage() .setName(subject) .setSubject(subject) .setBody(content) .setMessageType(MessageTypeEnum.NOTIFICATION); List<PamirsMessage> messages = new ArrayList<>(); messages.add(message); SystemMessage systemMessage = new SystemMessage(); systemMessage.setPartners(userIds.stream().map(i -> (PamirsUser) new PamirsUser().setId(i)).collect(Collectors.toList())) .setType(MessageGroupTypeEnum.SYSTEM_MAIL) .setMessages(messages); mailSender.sendSystemMail(systemMessage); return data; }

    2024年8月19日
    1.6K00
  • 如何跳过固定path路径下面所有的按钮权限

    场景: 业务上需要跳过弹窗打开里面的所有按钮权限。 实践: 实现AuthFilterService权限接口。 package pro.shushi.pamirs.top.api.spi; import org.apache.commons.lang3.StringUtils; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.spi.AuthFilterService; import pro.shushi.pamirs.boot.web.session.AccessResourceInfoSession; import pro.shushi.pamirs.meta.common.spi.SPI; /** * @author Yexiu at 09:04 on 2024/9/27 */ @Order(88) @Component @SPI.Service public class CustomAuthFilterService implements AuthFilterService { public static final String skipPath = "/top_demo/uiMenuc6238c29bca44250a041691565056a63/ACTION#top.Teacher#uiView2b60cc6daa334c7280cb78207d41addc"; @Override public Boolean isAccessAction(String model, String name) { String path = AccessResourceInfoSession.getInfo().getOriginPath(); if (StringUtils.isNotEmpty(path) && path.startsWith(skipPath)) { //返回true就代表通过验证 return true; } return null; } @Override public Boolean isAccessAction(String path) { if (StringUtils.isNotEmpty(path) && path.startsWith(skipPath)) { //返回true就代表通过验证 return true; } return null; } } 可以看到弹窗下面的按钮都不需要权限控制了。

    2025年3月11日
    1.2K00
  • 字段类型之关系描述的特殊场景(常量关联)

    场景概述 【字段类型之关系与引用】一文中已经描述了各种关系字段的常规写法,还有一些特殊场景如:关系映射中存在常量,或者M2M中间表是大于两个字段构成。 场景描述 1、PetTalent模型增加talentType字段2、PetItem与PetTalent的多对多关系增加talentType(达人类型),3、PetItemRelPetTalent中间表维护petItemId、petTalentId以及talentType,PetDogItem和PetCatItem分别重写petTalents字段,关系中增加常量描述。示意图如下: 实际操作步骤 Step1 新增 TalentTypeEnum package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.BaseEnum; @Dict(dictionary = TalentTypeEnum.DICTIONARY,displayName = "达人类型") public class TalentTypeEnum extends BaseEnum<TalentTypeEnum,Integer> { public static final String DICTIONARY ="demo.TalentTypeEnum"; public final static TalentTypeEnum DOG =create("DOG",1,"狗达人","狗达人"); public final static TalentTypeEnum CAT =create("CAT",2,"猫达人","猫达人"); } Step2 PetTalent模型增加talentType字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.TalentTypeEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(PetTalent.MODEL_MODEL) @Model(displayName = "宠物达人",summary="宠物达人",labelFields ={"name"}) public class PetTalent extends AbstractDemoIdModel{ public static final String MODEL_MODEL="demo.PetTalent"; @Field(displayName = "达人") private String name; @Field(displayName = "达人类型") private TalentTypeEnum talentType; } Step3 修改PetItem的petTalents字段,在关系描述中增加talentType(达人类型) @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = PetItemRelPetTalent.MODEL_MODEL ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step4 PetDogItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#1#"}, domain = " talentType == DOG") private List<PetTalent> petTalents; Step5 PetCatItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#2#"}, domain = " talentType == CAT") private List<PetTalent> petTalents; Step6 PetCatItem增加petTalents字段,many2one关系示例 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Model.model(PetPet.MODEL_MODEL) @Model(displayName…

    2024年5月25日
    2.3K00
  • 如何改变调度策略,让Schedule独立执行线程

    schedule里,相同的taskType跑多个业务任务,如果其中一个任务大量重试占满了调度线程,会影响别的业务任务及时被执行,如下面截图中,taskType用的是平台内置的常量,这个常量会被其他任务也使用,如果当前任务出现了异常占用了这个taskType的所有线程,那么这个taskType下面的其他任务就会被阻塞延后执行。应该给需要业务及时性的任务单独建立自定义的taskType,这样每个taskType的线程就是独立的,A任务异常不会影响B任务的执行。 1、后台创建task type相关的类,继承BaseScheduleNoTransactionTask,要加springbean的注解,参考:task type建议使用类名 2、提交任务的时候,设置tasktype为步骤1的TaskType 3、控制台新增策略和任务bean名称为步骤1的spring beanName,任务名称 $xxx,右边的占位符内容为yml里面配置的ownSign字段任务的名称也是步骤1的 spring beanName 4、配置完成后,控制台启动任务,就可以测试了

    2024年2月20日
    1.2K00

Leave a Reply

Please Login to Comment