度中心度:网络分析中最基础的影响力量化指标
2026/8/1 17:27:53
结合(Spring Security + MyBatis-Plus)以及数据中台的通用架构,梳理了一套完整的权限设计方案,包含架构分层、核心设计以及时序交互流程。
在数据中台中,权限体系通常分为三个维度,你提到的这三者各司其职:
project_id = X的过滤条件。@PreAuthorize注解配合 SpEL(Spring Expression Language)。UserDetailsService从数据库加载用户的角色和权限列表(如project:admin,data:query)。@PreAuthorize("hasAuthority('DATA_QUERY')")。@PreAuthorize("#projectId == authentication.projectId")来校验用户是否有权访问该特定项目。DataPermissionInterceptor或自定义InnerInterceptor。beforeQuery),拦截所有的SELECT语句。@DataScope)。currentProjectId,自动拼接AND project_id = ?。AND dept_id IN (1,2)。这是一个用户发起数据查询请求(例如:查询某项目下的销售报表)的完整时序图解:
/api/report/sales?projectId=100。JwtAuthenticationTokenFilter拦截请求,解析 Token,将用户信息(包含用户ID、角色列表、权限字符串)存入SecurityContextHolder。REPORT_VIEW)。@DataScope(deptAlias = "d", projectAlias = "p")。DataPermissionInterceptor拦截到即将执行的 SQL 查询。p.id = 100。d.region = 'EastChina'。SELECT * FROM sales s JOIN dept d ON s.dept_id = d.idSELECT * FROM sales s JOIN dept d ON s.dept_id = d.id WHERE p.id = 100 AND d.region = 'EastChina'。| 权限类型 | 控制粒度 | 核心技术 | 实现机制 | 典型场景 |
|---|---|---|---|---|
| 功能权限 | 菜单/按钮/API | Spring Security | @PreAuthorize注解 + 角色权限匹配 | 普通用户看不到“系统管理”菜单 |
| 项目权限 | 项目/工作空间 | MyBatis-Plus 拦截器 | SQL 拦截 +project_id自动注入 | 用户 A 只能看到项目 A 的数据,看不到项目 B |
| 数据权限 | 数据行/列 | MyBatis-Plus 拦截器 | SQL 拦截 + 动态 WHERE 条件拼接 | 销售员只能看自己的订单,经理看全组订单 |
1. MyBatis-Plus 拦截器核心逻辑
// 实现 InnerInterceptor 接口 public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 1. 获取当前用户信息 LoginUser user = SecurityUtils.getLoginUser(); // 2. 判断是否需要数据权限(排除管理员) if (user.isAdmin()) return; // 3. 获取注解配置的表别名 String deptAlias = getDataScopeAlias(ms, "dept"); String projectAlias = getDataScopeAlias(ms, "project"); // 4. 拼接 SQL 片段 StringBuilder sqlFilter = new StringBuilder(); // 项目权限:限制只能看自己加入的项目 sqlFilter.append(projectAlias).append(".id IN (").append(user.getProjectIds()).append(") "); // 数据权限:根据角色限制数据范围 (例如:仅本人、本部门) if ("dept".equals(user.getRole().getDataScopeType())) { sqlFilter.append(" OR ").append(deptAlias).append(".id = ").append(user.getDeptId()); } // 5. 将拼接好的 SQL 条件注入到查询参数中 // MyBatis-Plus 会自动将这个条件合并到原生 SQL 的 WHERE 后面 injectFilterToParameter(parameter, sqlFilter.toString()); }2. Service 层使用注解
@Service public class ReportService { // dataScope 注解指定了部门表别名和项目表别名 @DataScope(deptAlias = "d", projectAlias = "p") public List<SalesReport> getReports(Long projectId) { // 这里写正常的业务查询,无需手动写 project_id 和 dept_id 的过滤 // 拦截器会自动帮你加上 return salesMapper.selectReports(projectId); } }这套方案的优势在于解耦和透明:
WHERE project_id = ?,由拦截器统一处理。在实际落地时,先搭建好用户-角色-权限的管理后台,确保管理员可以灵活配置某个角色是“本部门数据”还是“全部数据”,并将这些配置存储在数据库中,供拦截器读取。