如何扩展行为权限

注意:5.2.8 以上版本适用

需求

我们的权限控制需要在页面上有交互才可以在管理中心控制该动作权限,在没有页面交互的动作是不能进行授权操作的。所以本文章将介绍如何将页面上没有交互的动作接入到系统权限中管理。

一、扩展系统权限的菜单页面

实现

步骤:

  1. 创建授权节点
    实现权限节点扩展接口: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", "自定义节点");
    }
    }
  2. 在管理中心中我们可以看到代码里创建的授权节点。
    如何扩展行为权限

  3. 给角色分配该动作的权限,调用我们配置的AuthTest模型的dataStatus动作看效果。
    如何扩展行为权限

    二、扩展菜单下的动作权限

    实现

    步骤:

  4. 创建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;
    }
    }
  5. 创建授权节点
    实现权限节点扩展接口: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(), viewAction.getName()】拼接的。和MODULE没有关系,这里MODULE可以自定义。
        if (viewAction != null) {
         root.getNodes().add(AuthNodeHelper.createViewActionNode(TopModule.MODULE_MODULE, viewAction, root));
        }
        nodes.add(0, root);
        return newNodes;
    }
    
    //创建权限组里的动作权限节点
    @Override
    public List<PermissionNode> buildNextPermissions(PermissionNode selected, List<PermissionNode> nodes) {
        //需要利用viewAction的path路径去判断当前选中节点是否是我们创建的自定义节点,返回为空则不向下处理授权节点。path路径是根据【viewAction.getModel(), viewAction.getName()】拼接的
        String path = "/" + AuthTest.MODEL_MODEL + "/" + "permissionExtension";
        if (!path.equals(selected.getPath())){
            return null;
        }
        List<PermissionNode> newNodes = new ArrayList<>();
        //从缓存中读取需要授权的Action
        List<Action> actions = new ArrayList<>();
        actions.add(PamirsSession.getContext().getExtendCache(ActionCacheApi.class).get(AuthTest.MODEL_MODEL, "dataStatus"));
        actions.add(PamirsSession.getContext().getExtendCache(ActionCacheApi.class).get(Teacher.MODEL_MODEL, "queryTea"));
        //将这些Action放入动作权限树用于授权
        //权限鉴权的path路径是根据【action.getModel(), action.getName()】拼接的。和TopModule.MODULE_MODULE没有关系,这里TopModule.MODULE_MODULE可以自定义。
        for (Action action : actions) {
            newNodes.add(AuthNodeHelper.createActionNode(TopModule.MODULE_MODULE, action, selected));
        }
        nodes.addAll(newNodes);
        return newNodes;
    }
    }
  6. 在管理中心中我们可以看到代码里创建的授权节点。
    如何扩展行为权限
    如何扩展行为权限

  7. 给角色分配该动作的权限,调用我们配置的AuthTest模型的dataStatus动作看效果。
    如何扩展行为权限

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

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

(0)
yexiu的头像yexiu数式员工
上一篇 2024年10月31日 pm9:58
下一篇 2024年11月1日 pm5:06

相关推荐

  • 模型定义在数据库中的映射

    模型定义在数据库中的映射 Oinone中通过定义模型来建立数据表,使用注解的方式来使多张表之间的关联。 数据库字段与模型定义字段映射 package pro.shushi.pamirs.top.api.model; import pro.shushi.pamirs.core.common.enmu.DataStatusEnum; 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.DateFormatEnum; import pro.shushi.pamirs.meta.enmu.DateTypeEnum; import pro.shushi.pamirs.meta.enmu.MimeTypeEnum; import java.math.BigDecimal; import java.util.Date; @Model.model(PamirsDemo.MODEL_MODEL) @Model(displayName = “PamirsDemo”) public class PamirsDemo extends IdModel { public static final String MODEL_MODEL = “top.PamirsDemo”; @Field.Binary(mime = MimeTypeEnum.html) @Field(displayName = “二进制类型”) private Byte[] byteType; @Field.Integer @Field(displayName = “整数”) private Long longType; @Field.Float @Field(displayName = “浮点数”) private BigDecimal floatType; @Field.Boolean @Field(displayName = “布尔类型”) private Boolean booleanType; @Field.Enum @Field(displayName = “枚举”) private DataStatusEnum enumType; @Field.String @Field(displayName = “字符串”) private String stringType; @Field.Text @Field(displayName = “多行文本”) private String textType; @Field.Html @Field(displayName = “富文本”) private String richText; @Field.Date(type = DateTypeEnum.DATE, format = DateFormatEnum.DATE) @Field(displayName = “日期类型”) private Date dataType; @Field.Date(type = DateTypeEnum.DATETIME, format = DateFormatEnum.DATETIME) @Field(displayName = “日期时间类型”) private Date dataTimeType; @Field.Money @Field(displayName = “金额”) private BigDecimal amount; } 更多字段基础请参考文档字段基础与复合 多对一的关系映射 例:设计一张教师表,一张科目表,教师表对科目表属于多对一的关系,在教师表中使用科目id管理关联关系。 教师表teacher 科目表professional 那么在Oinone的模型定义中,这两张表定义是这样的; 教师模型 package pro.shushi.pamirs.top.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.IdModel; @Model.model(Teacher.MODEL_MODEL) @Model(displayName = “教师”, summary = “教师”) public class Teacher extends IdModel { public static final String MODEL_MODEL = “top.Teacher”; @Field.String @Field(displayName = “教师名字”) private String teacherName; @Field.Integer @Field(displayName = “科目id”) private Long professionalId; @Field(displayName…

    2024年8月16日
    1.3K00
  • 非存储字段搜索,适应灵活的搜索场景

    1、非存储字段搜索 1.1 描述 通常根据本模型之外的信息作为搜索条件时,通常会把 这些字段放在代理模型上。这类场景我们称之为 非存储字段搜索 1.2 场景一 非存储字段为基本的String。1.代码定义:非存储字段为基本的包装数据类型 @Field(displayName = "确认密码", store = NullableBoolEnum.FALSE) private String confirmPassword; 2.设计器拖拽:列表中需要有退拽这个字段,字段是否隐藏的逻辑自身业务是否需要决定。3.页面通过非存储字段为基本的包装数据类型进行搜索时。会拼在 queryWrapper 的属性queryData中,queryData为Map,key为字段名,value为搜索值。4.后台逻辑处理代码示例: Map<String, Object> queryData = queryWrapper.getQueryData(); if (null != queryData) { Object productIdObj = queryData.get(PRODUCT_ID); if (Objects.nonNull(productIdObj)) { String productId = productIdObj.toString(); queryWrapper.lambda().eq(MesProduceOrderProxy::getProductId, productId); } } 1.3 场景二 非存储字段为非存储对象。 定义 为非存储的 @Field(displayName = "款", store = NullableBoolEnum.FALSE) @Field.many2one @Field.Relation(store = false) private MesProduct product; 2.页面在搜索栏拖拽非存储字段作为搜索条件 3.后台逻辑处理代码示例: try { if (null != queryData && !queryData.isEmpty()) { List<Long> detailId = null; BasicSupplier supplier = JsonUtils.parseMap2Object((Map<String, Object>) queryData.get(supplierField), BasicSupplier.class); MesProduct product = JsonUtils.parseMap2Object((Map<String, Object>) queryData.get(productField), MesProduct.class); MesMaterial material = JsonUtils.parseMap2Object((Map<String, Object>) queryData.get(materialField), MesMaterial.class); if (supplier != null) { detailId = bomService.queryBomDetailIdBySupplierId(supplier.getId()); if (CollectionUtils.isEmpty(detailId)) { detailId.add(-1L); } } if (product != null) { List<Long> produceOrderId = produceOrderService.queryProductOrderIdByProductIds(product.getId()); if (CollectionUtils.isNotEmpty(produceOrderId)) { queryWrapper.lambda().in(MesProduceBomSizes::getProduceOrderId, produceOrderId); } } if (material != null) { //找出两个bom列表的并集 List<Long> materBomDetailId = bomService.queryBomDetailIdByMaterialId(material.getId()); if (CollectionUtils.isNotEmpty(detailId)) { detailId = detailId.stream().filter(materBomDetailId::contains).collect(Collectors.toList()); } else { detailId = new ArrayList<>(); if (CollectionUtils.isEmpty(materBomDetailId)) { detailId.add(-1L); } else { detailId.addAll(materBomDetailId); } } } if (CollectionUtils.isNotEmpty(detailId)) { queryWrapper.lambda().in(MesProduceBomSizes::getProductBomId, detailId); } } } catch (Exception e) { log.error("queryData处理异常", e); } 注意:…

    2024年2月20日
    1.4K00
  • 代码示例:快速找到示例代码

    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.5K00
  • Nacos做为注册中心:如何调用其他系统的SpringCloud服务?

    Oinone项目引入Nacos作为注册中心,调用外部的SpringCloud服务 Nacos可以做为注册中心,提供给Dubbo和SpringCloud等微服务框架使用。 目前Oinone的底层使用的是Dubbo进行微服务的默认协议调用,但是我们项目如果存在需要调用其他系统提供的SpringCloud服务,Oinone其实并没有限制大家去这么写代码。 可以参考Nacos或SpringCloud的官方文档,只要不存在Jar包冲突等场景,很多的扩展其实大家都可以使用。 注意!!!Nacos、SpringCloud、SpringCloudAlibaba是有依赖版本严格要求的:点击查看 具体示例: 一、项目中增加依赖 主pom引入兼容的版本: <dependencyManagement> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.7.RELEASE</version> <!– 目前兼容的版本 –> <type>pom</type> <scope>import</scope> </dependency> </dependencyManagement> 使用模块的pom引入依赖: <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 二、 配置 application.yml spring: cloud: nacos: discovery: server-addr: localhost:8848 username: nacos password: nacos 三、启动类添加注解 @EnableDiscoveryClient @EnableFeignClients public class NacosConsumerApplication { public static void main(String[] args) { SpringApplication.run(NacosConsumerApplication.class, args); } } 四、验证 创建 Feign Client 接口 import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "nacos-demo") // 指定目标服务的名称 public interface ProviderClient { @GetMapping("/hello") String hello(); } 创建 Controller 调用 Feign Client @RestController public class ConsumerController { private final ProviderClient providerClient; public ConsumerController(ProviderClient providerClient) { this.providerClient = providerClient; } @GetMapping("/hello") public String hello() { return providerClient.hello(); } } 在浏览器中访问 http://localhost:8082/hello你应该会看到服务提供者返回的响应。

    2024年6月4日
    2.3K00
  • 读写分离

    总体介绍 Oinone的读写分离方案是基于Sharding-JDBC的整合方案,要先具备一些Sharding-JDBC的知识。 [Sharding-JDBC] 读写分离依赖于主从复制来同步数据,从库复制数据后,才能通过读写分离策略将读请求分发到从库,实现读写操作的分流,请根据业务需求自行实现主从配置。 配置读写策略 配置 top_demo 模块走读写分离的逻辑数据源 pamirsSharding。 配置数据源。 为 pamirsSharding 配置数据源以及 sharding 规则。 指定需要被sharding-jdbc接管的模块 指定top_demo模块给 Sharding-JDBC 接管,接管逻辑数据源名为 pamirsSharding pamirs: framework: data: ds-map: base: base top_demo: pamirsSharding 配置数据源 pamirs: datasource: pamirsMaster: driverClassName: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://127.0.0.1:3306/61_pamirs_mydemo_master?useSSL=false&allowPublicKeyRetrieval=true&useServerPrepStmts=true&cachePrepStmts=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true username: root password: ma123456 initialSize: 5 maxActive: 200 minIdle: 5 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true asyncInit: true pamirsSlaver: # 从库数据源配置 driverClassName: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://127.0.0.1:3306/61_pamirs_mydemo_slaver?useSSL=false&allowPublicKeyRetrieval=true&useServerPrepStmts=true&cachePrepStmts=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true username: root password: ma123456 initialSize: 5 maxActive: 200 minIdle: 5 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true asyncInit: true 配置读写数据源及规则 pamirs: sharding: define: data-sources: pamirsSharding: pamirsMaster # 为逻辑数据源pamirsSharding指向主数据源pamirsMaster。 models: "[trigger.PamirsSchedule]": tables: 0..13 rule: pamirsSharding: actual-ds: # 指定逻辑数据源pamirsSharding代理的数据源为pamirsMaster、pamirsSlaver – pamirsMaster – pamirsSlaver # 以下配置跟sharding-jdbc配置一致 replicaQueryRules: – data-sources: pamirsSharding: primaryDataSourceName: pamirsMaster # 写库数据源 replicaDataSourceNames: – pamirsSlaver # 读库数据源 loadBalancerName: round_robin load-balancers: round_robin: type: ROUND_ROBIN # 读写规则

    2025年5月22日
    58600

Leave a Reply

登录后才能评论