电商后台的权限管理远不止"管理员能看到所有菜单,运营只能看订单"—还要细到"北京门店的运营只能看到北京地区的订单"、"A部门的员工不能操作B部门的商品"。这就是RBAC(基于角色的访问控制)加上数据权限隔离要解决的问题。

一、RBAC五张表的标准设计

标准的RBAC模型至少需要五张表:

-- 用户表
CREATE TABLE sys_user (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    dept_id BIGINT,
    status TINYINT DEFAULT 1
);

-- 角色表
CREATE TABLE sys_role (
    id BIGINT PRIMARY KEY,
    role_name VARCHAR(50) NOT NULL,
    role_code VARCHAR(50) NOT NULL UNIQUE,
    data_scope TINYINT DEFAULT 1  -- 1:全部 2:本部门 3:本部门及以下 4:仅本人
);

-- 权限表(菜单+按钮)
CREATE TABLE sys_permission (
    id BIGINT PRIMARY KEY,
    parent_id BIGINT DEFAULT 0,
    perm_name VARCHAR(50) NOT NULL,
    perm_type TINYINT NOT NULL  -- 1:菜单 2:按钮
);

-- 用户-角色关联表
CREATE TABLE sys_user_role (
    user_id BIGINT NOT NULL,
    role_id BIGINT NOT NULL,
    PRIMARY KEY (user_id, role_id)
);

-- 角色-权限关联表
CREATE TABLE sys_role_permission (
    role_id BIGINT NOT NULL,
    perm_id BIGINT NOT NULL,
    PRIMARY KEY (role_id, perm_id)
);

这张表结构覆盖了90%的电商后台权限场景。data_scope字段是数据权限的关键——它决定了拥有该角色的用户能看到哪些数据范围。

二、Spring Security + JWT动态权限

Spring Security默认的hasRole('ADMIN')注解是硬编码的,角色一变就要改代码重新部署。动态权限的实现思路是:自定义FilterSecurityInterceptor,在每次请求时从数据库加载当前用户的权限列表,与请求URL匹配。

@Component
public class DynamicPermissionFilter extends OncePerRequestFilter {

    @Autowired
    private PermissionService permissionService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) {
        // 1. 从JWT中解析userId
        Long userId = JwtUtil.getUserIdFromToken(request);

        // 2. 从Redis/DB获取该用户的权限列表
        Set<String> permissions = permissionService.getPermissionsByUserId(userId);

        // 3. 匹配当前请求URL
        String requestURI = request.getRequestURI();
        boolean hasPermission = permissions.stream()
            .anyMatch(p -> pathMatcher.match(p, requestURI));

        if (!hasPermission) {
            response.setStatus(403);
            return;
        }
        chain.doFilter(request, response);
    }
}

权限列表建议缓存到Redis,设置合理的过期时间(如30分钟),避免每次请求都查数据库。权限变更时主动清除Redis缓存。

三、数据权限:部门隔离的实现

菜单权限控制"能不能看到这个页面",数据权限控制"在这个页面能看到哪些数据"。典型场景是:区域经理只能看到自己负责区域的订单和用户。

实现方式是在SQL查询层注入数据权限条件。自定义MyBatis拦截器或使用MyBatis-Plus的数据权限插件:

// 在SQL执行前,根据当前用户的数据权限范围追加WHERE条件
// 查询所有订单 → 自动变为 查询本部门+下级部门的订单
SELECT * FROM t_order WHERE dept_id IN (101, 102, 103)

数据权限的粒度设计需要和业务对齐:全部数据 > 本部门及下级 > 本部门 > 仅本人。不要追求过度的灵活性,四个级别足够覆盖99%的场景。

四、按钮级权限

菜单权限是粗粒度的,按钮权限是细粒度的。比如"订单列表"页面中,普通运营有"查看"按钮但没有"退款"按钮。前端用v-if根据权限列表控制按钮显示,后端在接口层再次校验——前端隐藏只是优化用户体验,真正的安全防线在后端。

五、前端路由的动态加载

Vue Router的addRoutes可以根据后端返回的菜单树动态注册路由。用户登录后,前端请求/api/user/menus接口,拿到该用户有权限的菜单列表,动态构建路由表。这样即使用户在浏览器地址栏输入了未授权的URL,路由表中不存在对应路由,自然跳转到404。

在搭建权限体系时,参考一些成熟的开源项目(如CRMEB中的RBAC实现)可以节省大量从零设计的时间。CRMEB的数据权限隔离方案中,SQL拦截器的设计尤其值得细读。

SEO小贴士

核心关键词:Java商城权限设计、Spring Security RBAC、电商后台权限、数据权限隔离

长尾词:JWT动态权限、MyBatis数据权限插件、按钮级权限控制、Vue动态路由权限

内容策略:从数据库设计到前后端实现全链路覆盖,搜索"RBAC"的用户通常需要完整方案

结构化优势:SQL建表语句 + Java代码 + 前端方案三位一体,搜索引擎更容易识别为高质量技术内容

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐