跳转到正文

快速开始

本例完成“创建菜单 → 创建带权限的角色 → 绑定另一位用户 → 查询用户权限”的同步流程。

前提是已有可运行的 EFC Web 应用,使用 Java 25、Spring Boot 4.1.0,依赖版本为 4.1.0-SNAPSHOT,所需构件已在项目 Maven 仓库或本地可用。准备 MySQL 数据源、驱动和两个已存在的身份:有菜单、角色和用户角色管理权限的管理员,以及用于验证的目标用户。管理员与目标用户必须不是同一用户。

本例不初始化首位管理员。空库首次部署时,需要先通过应用受控的数据初始化流程配置管理菜单、管理角色和管理员绑定,或沿用应用已有认证授权来源;不能用尚未授权的身份调用管理接口完成自举。管理权限编码见角色与授权

1. 添加依赖

在应用 POM 中保留已有 Business BOM,并额外导入菜单模块 POM。当前 Business BOM 未覆盖菜单 JAR 的版本,不能省略第二项。

xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.own</groupId>
            <artifactId>springboot-business-dependencies</artifactId>
            <version>4.1.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.own.business</groupId>
            <artifactId>springboot-business-system-menu</artifactId>
            <version>4.1.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.own.business</groupId>
        <artifactId>springboot-business-system-menu-controller-admin</artifactId>
    </dependency>
    <dependency>
        <groupId>com.own.business</groupId>
        <artifactId>springboot-business-system-menu-controller-app</artifactId>
    </dependency>
    <dependency>
        <groupId>com.own.component</groupId>
        <artifactId>springboot-component-store-starter-local</artifactId>
    </dependency>
</dependencies>

本地 Store 用于单实例演示;已有 Store 时沿用原实现,多实例使用共享后端。认证组件在存在 StoreClient 时自动创建 PermissionCacheService,菜单和角色 Service 必须能注入该服务。

UseBusinessSystemMenu 已注册到自动配置导入文件,扫描 com.own.business.system.menu 下的业务、已引入的 Controller 与 Mapper,不必重复添加扫描。应用还需启用认证、权限切面和 PageHelper 分页配置,使 SessionUserUtil 能取得可信用户 ID 与客户端。身份适配契约见 Authentication,菜单 Hook 的聚合与权限拦截仍需应用完整接入。

如果应用还没有 MapperUtil Bean,将以下配置放到启动类能扫描的包中:

java
package com.example.config;

import com.own.component.base.business.util.MapperUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration(proxyBeanMethods = false)
@Import(MapperUtil.class)
public class MenuApplicationConfiguration {
}

2. 准备数据库与配置

在尚未创建这些表的 MySQL 数据库中执行 系统菜单首次建表 SQL。脚本保留源码中的四张表及索引,移除了 DROP TABLE;它不插入管理员,不是已有数据库的增量迁移,也不会随应用启动自动执行。

沿用应用数据源。示例配置中的环境变量由部署环境提供:

yaml
spring:
  main:
    allow-circular-references: true
  datasource:
    url: ${SYSTEM_MENU_JDBC_URL}
    username: ${SYSTEM_MENU_DB_USERNAME}
    password: ${SYSTEM_MENU_DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver

当前角色、角色权限和用户角色 Service 存在相互字段注入,使用原实现时需允许循环引用。上述设置是应用级兼容配置,并非菜单业务开关。模块没有独立的 own.system-menu.* 配置。

3. 创建菜单和角色

启动应用后,使用管理员身份的 API 客户端请求。以下路径应补上应用域名、端口与 context path;认证头或 Cookie 沿用应用协议,JSON 请求使用 Content-Type: application/json

先调用 POST /api/admin/system/menu/

json
{
  "code": "report-demo",
  "title": "演示报表",
  "parentId": null,
  "path": "/reports/demo",
  "clientId": "web",
  "isDisable": 0,
  "isShow": 1,
  "sortOrder": 10,
  "permission": "查询"
}

web 替换成目标用户登录身份实际返回的 client(),它不由 /api/app/ 路径决定。保存响应 data.id 作为菜单 ID。菜单路径只是元数据,本例不会生成前端报表页面。

再调用 POST /api/admin/system/role/。下面的 10001 必须替换为刚创建的菜单 ID,clientId 与菜单保持一致:

json
{
  "name": "报表查看者",
  "clientId": "web",
  "isDisable": 0,
  "permissions": [
    { "menuId": "10001", "permission": "查询" }
  ]
}

保存响应 data.id 作为角色 ID。角色新增会写入权限,但新增响应不会回查填充 permissions;用 GET /api/admin/system/role/{roleId} 核对详情中的权限记录。

4. 绑定目标用户

调用 POST /api/admin/system/user/role/bind,将 20001 替换为目标用户 ID、30001 替换为角色 ID:

json
{
  "userIdList": ["20001"],
  "roleIdList": ["30001"],
  "bindType": "append-update"
}

追加模式保留用户已有角色。成功业务码为 00000,本接口成功时无业务数据,不要求 data=true。再请求 GET /api/admin/system/user/role/bind/{userId},确认返回列表包含新角色 ID。

5. 切换身份并验证

使用目标用户已登录的会话依次发起以下无请求体的 GET:

请求预期结果
/api/app/system/role/selfdata 中有名称为“报表查看者”、客户端匹配的角色
/api/app/system/role/permission/selfdata 中有新角色 ID、菜单 ID 和 permission=查询 的权限记录
/api/app/system/menu/alldata 中“演示报表”的 permission 为“查询”
/api/app/system/menu/tree通过节点的 item 读取菜单字段,children 表示下级节点

切换到同客户端且未获该权限的用户时,角色与权限接口不应包含本例角色及授权;菜单 /all 仍可返回该菜单,其 permission 应为空字符串。不要用“菜单是否出现”代替权限验证,原因见当前用户查询

验证撤权时,由管理员调用 POST /api/admin/system/user/role/unbind,请求体使用第 4 步的 userIdListroleIdList,去掉 bindType。再次查询目标用户权限;若用户还从其他角色获得相同权限,该权限仍然保留。

后续维护见菜单管理角色与授权。本例验证的是权限数据流,实际报表接口还需要应用声明并启用对应认证权限检查。