如何扩展行为权限

注意: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

相关推荐

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

    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.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日
    47300
  • 如何把平台功能菜单加到项目中?

    概述 在使用Oinone低代码平台进行项目开发的过程中,会存在把平台默认提供的菜单加到自己的项目中。这篇文章介绍实现方式,以角色管理为例。 1. 低代码情况 即项目是通过后端代码初始化菜单的方式。 通常在 XXXMenu.java类通过@UxMenu注解的方式,代码片段如下: 此种情况与添加项目本地的菜单无区别,具体代码: @UxMenu(value = "账号管理", icon = "icon-yonghuguanli") class AccountManage { @UxMenu("用户管理") @UxRoute(value = CustomerCompanyUserProxy.MODEL_MODEL, title = "用户管理") class CompanyUserManage { } @UxMenu("角色管理") @UxRoute(value = AuthRole.MODEL_MODEL, module = AdminModule.MODULE_MODULE, title = "角色管理") class RoleManage { } @UxMenu("操作日志") @UxRoute(value = OperationLog.MODEL_MODEL, module = AdminModule.MODULE_MODULE/***菜单所挂载的模块**/) class OperateLog { } } 2. 无代码情况 在界面设计器中,新建菜单–>选择绑定已有页面,进行发布即可。

    2024年4月19日
    1.6K00
  • 动态视图支持权限配置

    动态页面在前端自定义之后,目前是没办法通过权限去控制路由页面的权限的,本篇文章将介绍如何解决这个问题。阅读本篇文章之前,应已经实现自定义页面渲染动态视图。参考文章:自定义视图内部渲染动态视图 实现思路: 通过自定义页面里设计的组件api名字,获取配置的路由页面名字。 解析路由页面包含的所有动作,拼接权限节点。 将路由页面的权限节点,拼到自定义视图所在菜单的权限管理上。 效果,路由页面的动作出现在自定义视图所在的菜单权限节点上 代码示例 package pro.shushi.pamirs.top.core.auth; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.entity.node.ActionPermissionNode; import pro.shushi.pamirs.auth.api.entity.node.PermissionNode; import pro.shushi.pamirs.auth.api.extend.load.PermissionNodeLoadExtendApi; import pro.shushi.pamirs.auth.api.loader.visitor.AuthCompileContext; import pro.shushi.pamirs.auth.api.loader.visitor.AuthCompileVisitor; import pro.shushi.pamirs.auth.api.loader.visitor.DslParser; import pro.shushi.pamirs.auth.api.pmodel.AuthResourceAuthorization; import pro.shushi.pamirs.auth.api.utils.AuthAuthorizationHelper; import pro.shushi.pamirs.boot.base.model.Menu; import pro.shushi.pamirs.boot.base.model.View; import pro.shushi.pamirs.boot.base.model.ViewAction; import pro.shushi.pamirs.boot.base.ux.model.UIView; import pro.shushi.pamirs.boot.base.ux.model.UIWidget; import pro.shushi.pamirs.boot.base.ux.model.view.UIField; import pro.shushi.pamirs.boot.web.loader.path.AccessResourceInfo; import pro.shushi.pamirs.boot.web.loader.path.ResourcePath; import pro.shushi.pamirs.boot.web.manager.MetaCacheManager; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.top.api.TopModule; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @Component @Order(88) @SPI.Service @Slf4j public class MyTestNodeLoadExtend implements PermissionNodeLoadExtendApi { @Autowired protected MetaCacheManager metaCacheManager; @Override public List<PermissionNode> buildNextPermissions(PermissionNode selected, List<PermissionNode> nodes) { // 动态页面所属的菜单name List<String> menuNames = new ArrayList<>(); menuNames.add("uiMenu3c094a75bd88461ba0ad780825069b32"); // 自定义组件api名称 List<String> apiNames = new ArrayList<>(); apiNames.add("dynamicView"); List<ActionPermissionNode> newNodes = new ArrayList<>(); for (String menuName : menuNames) { List<ActionPermissionNode> actionPermissionNodes = buildActionPermissionNodes(selected, menuName, apiNames); if (CollectionUtils.isNotEmpty(actionPermissionNodes)) { newNodes.addAll(actionPermissionNodes); } } nodes.addAll(newNodes); return nodes; } private List<ActionPermissionNode> buildActionPermissionNodes(PermissionNode selected, String menuName, List<String> apiNames) { String path = ResourcePath.generatorPath(TopModule.MODULE_MODULE, menuName); if (!path.equals(selected.getPath())) { return null; } Menu menu = metaCacheManager.fetchCloneMenus(TopModule.MODULE_MODULE).stream() .filter(v -> v.getName().equals(menuName)) .findFirst() .orElse(null); if (menu == null) { return null; } menu.fieldQuery(Menu::getViewAction); View mainView = fetchMainView(menu.getViewAction()); if (mainView == null) { return null; }…

    2025年9月10日
    75800
  • 如何给角色增加菜单权限

    对接第三方的权限时,第三方传过来菜单项,需要拿着这些菜单在平台这边进行授权,可以使用代码的方式给指定菜单创建权限代码示例: public class demo { @Autowired private PermissionNodeLoader permissionNodeLoader; @Autowired private AuthRbacRolePermissionServiceImpl authRbacRolePermissionService; public void roleAuthorization() { ArrayList<Menu> menus = new ArrayList<>(); menus.add(new Menu().queryOneByWrapper(Pops.<Menu>lambdaQuery() .from(Menu.MODEL_MODEL) .eq(Menu::getName, "uiMenu90dd10ae7cc4459bacd2845754b658a8") .eq(Menu::getModule, TopModule.MODULE_MODULE))); menus.add(new Menu().queryOneByWrapper(Pops.<Menu>lambdaQuery() .from(Menu.MODEL_MODEL) .eq(Menu::getName, "TopMenus_shoppMenu_Shop3Menu_ShopSayHello52eMenu") .eq(Menu::getModule, TopModule.MODULE_MODULE))); //加载指定角色的全部资源权限项 ResourcePermissionNodeLoader loader = permissionNodeLoader.getManagementLoader(); List<PermissionNode> nodes = loader.buildRootPermissions(); List<AuthRbacResourcePermissionItem> authRbacRolePermissionProxies = new ArrayList<>(); //给指定角色创建权限,如果需要多个角色,可以for循环依次执行authRbacRolePermissionService.update(authRbacRolePermissionProxy) AuthRole authRole = new AuthRole().queryOneByWrapper(Pops.<AuthRole>lambdaQuery() .from(AuthRole.MODEL_MODEL) .eq(AuthRole::getCode, "R003") .eq(AuthRole::getName, "R003")); AuthRbacRolePermissionProxy authRbacRolePermissionProxy = new AuthRbacRolePermissionProxy(); AuthRole.transfer(authRole, authRbacRolePermissionProxy); for (PermissionNode node : nodes) { traverse(node, authRbacRolePermissionProxies, menus); } authRbacRolePermissionProxy.setResourcePermissions(authRbacRolePermissionProxies); authRbacRolePermissionService.update(authRbacRolePermissionProxy); } private void traverse(PermissionNode node, List<AuthRbacResourcePermissionItem> authRbacRolePermissionProxies, ArrayList<Menu> menus) { if (node == null) { return; } //按照指定菜单进行过滤,如果不是指定菜单,则设置菜单项不可访问,如果是指定菜单,则设置可访问 Set<Long> menuIds = new HashSet<>(); for (Menu menu : menus) { menuIds.add(menu.getId()); } if (node instanceof MenuPermissionNode) { AuthRbacResourcePermissionItem item = new AuthRbacResourcePermissionItem(); if (menuIds.contains(Long.parseLong(node.getId()))) { item.setCanAccess(Boolean.TRUE); } else { item.setCanAccess(Boolean.FALSE); } item.setCanManagement(node.getCanManagement()); item.setPath(node.getPath()); item.setSubtype(node.getNodeType()); item.setType(AuthEnumerationHelper.getResourceType(node.getNodeType())); item.setDisplayName(node.getDisplayValue()); item.setResourceId(node.getResourceId()); authRbacRolePermissionProxies.add(item); } List<PermissionNode> childNodes = node.getNodes(); if (CollectionUtils.isNotEmpty(childNodes)) { for (PermissionNode child : childNodes) { traverse(child, authRbacRolePermissionProxies, menus); } } } } 执行看效果

    2024年11月14日
    88800

Leave a Reply

登录后才能评论