对字段进行加密存储

需求:

模型字段上使用 pro.shushi.pamirs.user.api.crypto.annotation.EncryptField 注解
模型动作上使用 pro.shushi.pamirs.user.api.crypto.annotation.NeedDecrypt 注解

示例:

对需要加密的字段添加@EncryptField注解

@Model.model(Student.MODEL_MODEL)
@Model(displayName = "学生", summary = "学生")
public class Student extends IdModel {

    public static final String MODEL_MODEL = "top.Student";

    @Field(displayName = "学生名字")
    @Field.String
    private String studentName;

    @Field(displayName = "学生ID")
    @Field.Integer
    private Long studentId;

    @Field(displayName = "学生卡号")
    @Field.String
    @EncryptField
    private String studentCard;
}

对函数添加@NeedDecrypt注解

   @Action.Advanced(name = FunctionConstants.create, managed = true)//默认取的是方法名
    @Action(displayName = "确定", summary = "添加", bindingType = ViewTypeEnum.FORM)
    @Function(name = FunctionConstants.create)//默认取的是方法名
    @Function.fun(FunctionConstants.create)//默认取的是方法名
    @NeedDecrypt
    public Student create(Student data) {
        String studentCard = data.getStudentCard();
        if (studentCard != null) {
        //自定义加密方法
            data.setStudentCard(StudentEncoder.encode(studentCard));
        }
        return data.create();
    }

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

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

(0)
yexiu的头像yexiu数式员工
上一篇 2024年10月9日 pm5:26
下一篇 2024年10月10日 pm9:55

相关推荐

  • 代码示例:快速找到示例代码

    1、图表设计器 数据可视化能加载到的接口,方法上需要加 category = FunctionCategoryEnum.QUERY_PAGE @Model.model(ExpensesIncome.MODEL_MODEL) @Component @Slf4j public class ExpensesIncomeAction { @Function.Advanced(type = FunctionTypeEnum.QUERY,category = FunctionCategoryEnum.QUERY_PAGE) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.API}) public Pagination<ExpensesIncome> queryPage(Pagination<ExpensesIncome> page, IWrapper<ExpensesIncome> queryWrapper) { page = new ExpensesIncome().queryPage(page, queryWrapper); if (page!=null && CollectionUtils.isNotEmpty(page.getContent())) { page.getContent().forEach(a->{ if (a.getBudgetInCome()!=null && a.getBudgetInCome().compareTo(new BigDecimal("0.0"))!=0) { if (a.getRetailAmount()!=null) { a.setInComeRate(a.getRetailAmount().divide(a.getBudgetInCome(),2)); } } }); } return page; } } 2、事务支持 1)对于单个系统(InJvm)/模型内部采用强事务的方式。比如:在库存转移时,库存日志和库存数量的变化在一个事务中,保证两个表的数据同时成功或者失败。2)强事务管理采用编码式,Oinone事务管理兼容Spring的事务管理方式;有注解的方式和代码块的方式。a) 使用注解的方式: @Override @Transactional public void awardOut(AwardBookOutEmpRecordDetail detail) { List<OutEmpRecordSubjectDetail> subjectDetails = detail.getSubjectDetails(); …… } 重要说明基于注解的事务,Bean和加注解的方法必须能被Spring切面到。 b) 使用编码方式: //组装数据/获取数据/校验 Tx.build(new TxConfig()).executeWithoutResult(status -> { // 1、保存部门数据 deliDepartmentService.createOrUpdateBatch(departments); //2、如果父进行了下级关联设置,处理下级关联设置的逻辑 for (DeliDepartment department : departments) { doDepartSubLink(department); } }); ………… 3)对于分布式事务采用最终数据一致性,借助【可靠消息】或者【Job】的方式来实现。 3、平台工具类使用 3.1 FetchUtil pro.shushi.pamirs.core.common.FetchUtil,重点关注 # 根据Id查询列表 pro.shushi.pamirs.core.common.FetchUtil#fetchMapByIds # 根据Id查询,返回Map pro.shushi.pamirs.core.common.FetchUtil#fetchListByIds # 根据Codes查询列表 pro.shushi.pamirs.core.common.FetchUtil#fetchMapByCodes # 根据Code查询,返回Map pro.shushi.pamirs.core.common.FetchUtil#fetchListByCodes # 根据Entity查询 pro.shushi.pamirs.core.common.FetchUtil#fetchOne 其他更多方法参考这个类的源代码 3.2 ListUtils pro.shushi.pamirs.meta.common.util.ListUtils # 把输入的List,按给定的长度进行分组 pro.shushi.pamirs.meta.common.util.ListUtils#fixedGrouping # 从给定的List中返回特定字段的数组,忽略Null并去重 pro.shushi.pamirs.meta.common.util.ListUtils#transform 4、枚举获取 根据枚举的name获取valueInteger value = ArchivesConfigTypeEnum.getValueByName(ArchivesConfigTypeEnum.class,“status1”);根据枚举的name获取枚举项ArchivesConfigTypeEnum typeEnum = TtypeEnum.getEnum(ArchivesConfigTypeEnum.class, “status1”); 5、Map类型的数据怎么定义,即ttype 为map的字段写法 @Field(displayName = "上下文", store = NullableBoolEnum.TRUE, serialize = JSON) @Field.Advanced(columnDefinition = "TEXT") private Map<String, Object> context; 6、后端获取前端请求中的variables PamirsRequestVariables requestVariables = PamirsSession.getRequestVariables(); 7、为什么有时候调用服务走远程了 1、假设: A模型(modelA)所在的模块(ModuleA) B模型(modelB)所在的模块(ModuleB) 部署方式决定调用方式: 1)部署方式1: ModuleA 和 ModuleB 部署在一个jvm中,此时: modelA的服务/页面去调用 modelB,因为部署在一起,直接走inJvm 2、部署方式2: ModuleA 和 ModuleB 部署在不同的jvm中; modelA的服务/页面去调用 modelB,则会走远程(RPC);此时需要: a)ModuleB 需要开启dubbo服务,且ModuleA 和 ModuleB注册中心相同…

    2024年2月20日
    1.4K00
  • 同一行操作跳转到不同的视图(动态表单)

    背景 实际项目中,存在这样的场景:同一列表中的数据是泛化的数据集合,即数据来源于不同的模型;行操作需要根据来源去向不同的目标页。 如下图,「提报」操作需根据「报表类型」去向不同的表单。 并支持目标弹窗标题和弹框大小的配置。 解决思路 每行记录需要跳转到不同的模型不同视图,增加一个配置页面用于维护源模型和目标模型的调整动作关系; 返回数据的时候,同时返回自定义的动作。 前端自定义实现如上面图例中的「填报」,从返回数据中获取ViewAction并做对应的跳转。 具体步骤 [后端] 建立模型和视图的关系设置的模型 1、创建 模型和视图的关系设置的模型,用于配置列表模型和各记录即目标模型的视图关系 import pro.shushi.oinone.examples.simple.api.proxy.system.SimpleModel; import pro.shushi.oinone.examples.simple.api.proxy.system.SimpleModule; import pro.shushi.pamirs.boot.base.enmu.ActionTargetEnum; import pro.shushi.pamirs.boot.base.model.View; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.IdModel; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; /** * 模型和视图的关系设置 * ModelRelViewSetting */ @Model.model(ModelRelViewSetting.MODEL_MODEL) @Model(displayName = "模型和视图的关系设置", summary = "模型和视图的关系设置") @Model.Advanced(unique = {"oModel,model,target,viewType,viewName"}) public class ModelRelViewSetting extends IdModel { public static final String MODEL_MODEL = "examples.custom.ModelRelViewSetting"; @Field.many2one @Field(displayName = "模块") @Field.Relation(relationFields = {"module"}, referenceFields = {"module"}) private SimpleModule moduleDef; @Field.String @Field(displayName = "模块编码", invisible = true) private String module; @Field.many2one @Field(displayName = "源模型") @Field.Relation(relationFields = {"oModel"}, referenceFields = {"model"}) private SimpleModel originModel; @Field.String @Field(displayName = "源模型编码", invisible = true) private String oModel; @Field.many2one @Field(displayName = "目标模型") @Field.Relation(relationFields = {"model"}, referenceFields = {"model"}) private SimpleModel targetModel; @Field.String @Field(displayName = "目标模型编码", invisible = true) private String model; @Field.Enum @Field(displayName = "视图类型") private ViewTypeEnum viewType; @Field.Enum @Field(displayName = "打开方式", required = true) private ActionTargetEnum target; @Field.String @Field(displayName = "动作名称", invisible = true) private String name; @Field.many2one @Field.Relation(relationFields = {"model", "viewName"}, referenceFields = {"model", "name"}, domain = "systemSource=='UI'") @Field(displayName = "绑定页面", summary = "绑定页面") private View view; @Field.String @Field(displayName = "视图/页面", invisible…

    2025年2月19日
    91300
  • 如何扩展行为权限

    注意:5.2.8 以上版本适用 需求 我们的权限控制需要在页面上有交互才可以在管理中心控制该动作权限,在没有页面交互的动作是不能进行授权操作的。所以本文章将介绍如何将页面上没有交互的动作接入到系统权限中管理。 一、扩展系统权限的菜单页面 实现 步骤: 创建授权节点实现权限节点扩展接口:pro.shushi.pamirs.auth.api.extend.load.PermissionNodeLoadExtendApi#buildRootPermissions @Component @Order(88) public class MyTestNodeLoadExtend implements PermissionNodeLoadExtendApi { public static final String MODEL = AuthTest.MODEL_MODEL; public static final String MODULE = TopModule.MODULE_MODULE; public static final String FUN = "dataStatus"; @Override public List<PermissionNode> buildRootPermissions(PermissionLoadContext loadContext, List<PermissionNode> nodes) { //创建授权根节点 PermissionNode root = createMyNode(); List<PermissionNode> newNodes = new ArrayList<>(); //从缓存中读取需要授权的Action Action cacheAction = PamirsSession.getContext().getExtendCache(ActionCacheApi.class).get(MODEL, FUN); if (cacheAction != null) { //将该Action放入权限树 //权限鉴权的path路径是根据【cacheAction.getModel() + cacheAction.getName()】拼接的。和MODULE没有关系,这里MODULE可以自定义。 AuthNodeHelper.addNode(newNodes, root, AuthNodeHelper.createActionNode(MODULE, cacheAction, root)); } nodes.add(0, root); return newNodes; } private PermissionNode createMyNode() { return AuthNodeHelper.createNodeWithTranslate("MyNode", "自定义节点"); } } 在管理中心中我们可以看到代码里创建的授权节点。 给角色分配该动作的权限,调用我们配置的AuthTest模型的dataStatus动作看效果。 二、扩展菜单下的动作权限 实现 步骤: 创建viewAction用于作为权限菜单"permissionExtension"是自定义的viewAction的name,用于下面拼path路径鉴权。因为这里只需要在系统权限那边利用这个viewAction创建出授权节点。所以”权限扩展form“可以随意定义名字,系统会拿默认视图。 @Model.model(AuthTest.MODEL_MODEL) @Component @UxRouteButton( action = @UxAction(name = "permissionExtension", displayName = "权限扩展", label = "权限扩展", contextType = ActionContextTypeEnum.CONTEXT_FREE), value = @UxRoute(model = AuthTest.MODEL_MODEL, viewName = "权限扩展form", openType = ActionTargetEnum.ROUTER)) public class AuthTestAction { @Action(displayName = "启用", contextType = ActionContextTypeEnum.SINGLE) public AuthTest dataStatus(AuthTest data) { data.setOrgName("给个值"); return data; } } 创建授权节点实现权限节点扩展接口:pro.shushi.pamirs.auth.api.extend.load.PermissionNodeLoadExtendApi#buildRootPermissions @Component @Order(88) public class MyTestNodeLoadExtend implements PermissionNodeLoadExtendApi { //创建授权根节点 @Override public List<PermissionNode> buildRootPermissions(PermissionLoadContext loadContext, List<PermissionNode> nodes) { PermissionNode root = AuthNodeHelper.createNodeWithTranslate("CustomNode", "自定义节点"); List<PermissionNode> newNodes = new ArrayList<>(); newNodes.add(root); ViewAction viewAction = new ViewAction().setModel(AuthTest.MODEL_MODEL).setName("permissionExtension").queryOne(); //将该Action放入权限树 //权限鉴权的path路径是根据【viewAction.getModel(),…

    2024年11月1日
    92900
  • mybatis拦截器的使用

    场景:自定义拦截器做数据的加解密。 注册自定义拦截器 @Configuration public class MyBatisConfig { // TODO: 注册自定义拦截器 @Bean @Order(999) public EncryptionInterceptor encryptionInterceptor() { return new EncryptionInterceptor(); } } 使用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);…

    2024年12月2日
    1.1K00
  • 系统图标使用自定义CDN地址(内网部署)

    在实际项目中,客户网络环境不能访问外网即纯内网部署。此时需要将所有的静态资源都放在客户内部的CDN上,该篇详细说明实现步骤。 实现步骤 1、把图片等静态资源上传到本地CDN上(如MINIO、Nginx等),图片等静态资源找 数式支持人员 提供; 【注意】:MINIO情况,放置图片等静态资源的桶权限需设置为公共读; 2、项目中YAML的OSS配置,使用本地CDN、并指定使用的本地CDN图标的标识appLogoUseCdn: true, OSS配置参考如下: 本地CDN使用MINIO(仅示例需根据实际情况修改) cdn: oss: name: MINIO type: MINIO # MINIO的配置根据实际情况修改 bucket: pamirs(您的bucket) # 上传和下载地址根据实际情况修改 uploadUrl: http://39.103.145.77:9000 downloadUrl: http://39.103.145.77:9000 accessKeyId: 您的accessKeyId accessKeySecret: 您的accessKeySecret # mainDir对用CDN的图片目录,根据项目情况自行修改 mainDir: upload/demo/ validTime: 3600000 timeout: 600000 active: true referer: # 使用客户自己的CDN的图片,否则系统默认的从数式的CDN中获取 appLogoUseCdn: true 或本地CDN使用Nginx(仅示例需根据实际情况修改) cdn: oss: name: 本地文件NG系统 type: LOCAL bucket: # uploadUrl 这个是Oinone后端服务地址和端口 uploadUrl: http://192.168.0.129:8099 # downloadUrl前端地址,即直接映射在nginx的静态资源的路径和端口 downloadUrl: http://192.168.0.129:9999 validTime: 3600000 timeout: 600000 active: true referer: # 本地Nginx静态资源目录 localFolderUrl: /opt/pamirs/static # 使用客户自己的CDN的图片,否则系统默认的从数式的CDN中获取 appLogoUseCdn: true 3、前端工程3.1 前端源码工程,在.evn中把 STATIC_IMG地址进行修改;http(https)、IP和端口改成与CDN对应的配置,URL中/oinone/static/images是固定的;例如: 本地CDN使用MINIO(仅示例需根据实际情况修改) STATIC_IMG: 'http://39.103.145.77:9000/pamirs(这里替换为OSS中的bucket)/oinone/static/images' 或本地CDN使用Nginx(仅示例需根据实际情况修改) STATIC_IMG: 'http://192.168.0.129:9999/static/oinone/static/images' 3.2 对于已经打包好的前端资源对于已打包好的前端资源即无法修改.evn的情况;需在前端资源的根目录,新建config/manifest.js. 如果已存在则不需要新建,同时原来的内容也不需要删除(追加即可),需增加的配置: 本地CDN使用MINIO(仅示例需根据实际情况修改) runtimeConfigResolve({ STATIC_IMG: 'http://39.103.145.77:9000/pamirs(这里替换为OSS中的bucket)/oinone/static/images', plugins: { usingRemote: true } }) 或本地CDN使用Nginx(仅示例需根据实际情况修改) runtimeConfigResolve({ STATIC_IMG: 'http://192.168.0.129:9999/static/oinone/static/images', plugins: { usingRemote: true } })

    2025年2月8日
    1.2K00

Leave a Reply

登录后才能评论