资讯动态

Element Admin后台权限管理系统与CMS内容管理实践解析

发布时间:2026/9/9 19:45:19 来源:尧图企业网站定制
接手过那么多管理后台项目我一直有个很深的感触权限管理和内容管理看着是两件事但在真实业务里它们总是绑在一起出现。后台这边要控制谁能进哪个菜单、谁能点哪个按钮前台那边要有人发文章、传图片、管理分类两边还共用一套账号体系。所以当团队决定用Element Admin这套方案同时解决权限和CMS两块需求时我是很支持的因为这正好戳中了大多数中后台项目的核心痛点。这篇文章就围绕ElementAdmin后台权限管理系统CMS管理系统这个组合来写面向两类人一类是准备从零搭建后台管理系统的Vue开发者另一类是已经跑通Demo但卡在权限设计、CMS内容模型这些细节上的同学。我把整个过程中的关键设计思路、实操步骤、踩坑记录都整理出来尽量让文章能直接照着落地。1. 为什么几乎每个Vue团队最终都会绕回Element Admin先聊一个可能被很多人忽略的事实市面上开源后台管理模板多到数不清但Element Admin这个系列的生命力一直很顽强。不是因为它代码写得有多惊艳而是它刚好站在一个非常稳定的交叉点上——Vue的响应式机制、Element UI的组件生态、以及中后台页面的固有交互模式。1.1 从零手写后台和基于模板改造差了不止一个数量级我自己刚入行那会儿也干过从零写后台的事说实话挺累的。登录页、布局框架、侧边栏折叠、面包屑导航、表格分页、弹窗表单、树形选择器这些组件逻辑看起来不难但串起来之后每个细节都在消耗时间。尤其是菜单权限和按钮权限要同时生效需要路由守卫、动态路由表、指令系统多层配合稍不留意就顾此失彼。Element Admin这类模板的价值在于它把这层和业务无关的底层基础设施提前打磨过了。你要做的不是重新发明轮子而是理解它的轮子是怎么转的然后把自己的业务组件装上去。对于大多数中小团队来说这是性价比最高的路没有之一。1.2 这个组合到底解决了什么问题把ElementAdmin后台权限管理系统和CMS管理系统放在一起看本质上是做了一套完整的“内容运营中台”。它同时覆盖了三层诉求访问控制层解决“谁能进入系统、谁能看到哪些页面、谁能操作哪些按钮”的权限模型问题内容生产层解决“文章、分类、标签、封面图、发布状态”等内容数据的结构化建模问题协作流程层解决“编辑提交、审核通过、定时发布、下线归档”这类多人协作的流程状态问题。这三层诉求对应到技术上就是动态路由、状态机、指令权限、RBAC模型这些核心概念。下面我把每一块拆开讲尽量讲细一点因为真正的坑往往不在概念本身而在概念落地的过程中。2. 权限系统不是“登录后放行”而是从按钮级权限到动态路由的全链路设计不少同学理解的权限系统就是“登录之后根据角色生成不同的菜单”这个理解不够完整。菜单只是权限的可见性维度真正难的是操作维度和数据维度。在Element Admin实际的开发中我通常把权限链路拆成四个层次一层层去实现。2.1 第一层登录认证与用户会话的建立登录流程在Element Admin里一般做成这样前端拿到用户名和密码调后台的登录接口后台验证通过后返回一个token前端把这个token存到cookie或者localStorage里后续所有请求都通过请求拦截器自动带上。这里有几个细节容易出现脏数据的问题token过期时间个人建议token有效期设短一点比如2小时配合refresh_token做无感续期。只用一个长期有效的token虽然开发时省事但一旦泄漏问题会很大。用户信息缓存登录成功后不要把用户的所有信息都塞到vuex里尤其是角色和权限列表这种会变的数据。正确做法是只存一个用户唯一标识菜单和权限通过接口实时获取。// vuex中推荐保存的结构 const state { token: getToken(), userId: null, name: , avatar: , roles: [], // 每次刷新重新拉取不从storage恢复 permissions: [] // 按钮级权限码列表同样实时获取 }很多项目在刷新页面之后菜单闪一下或者直接白屏就是因为用户信息被持久化到了localStorage刷新时从本地恢复导致整个权限链路对不上。2.2 第二层角色-菜单-权限码的表驱动设计后端的角色模型通常是经典的RBAC基于角色的访问控制用户关联角色角色关联菜单和权限资源。落到前端我们需要做的就是拿到当前的菜单列表和权限码列表然后动态生成路由。菜单表的设计有一些通用的字段建议在数据库层面就规划好字段说明示例menu_id菜单唯一标识101parent_id父级菜单ID0表示顶级path前端路由路径/content/articlecomponent前端组件路径content/article/indextitle菜单名称文章管理icon菜单图标el-icon-documentsort_order排序值1perms_code权限码content:article:listvisible是否显示在侧边栏1这个表不是只给后端用的前端拿到返回结果后只需要做一个简单的递归构建就能生成路由和菜单树。2.3 第三层动态路由的加载时机和刷新保活动态路由的核心原则是路由表不是写死在代码里的而是用户登录之后根据权限数据现拼出来的。在Vue Router 4.x版本下比较稳的做法是使用addRoute逐个添加。这里面的一个关键操作是在路由守卫中判断当前路由表是否已经初始化如果没有就先拉取权限数据、生成路由表、再放行如果已经初始化则直接放行。刷新页面白屏是动态路由最常见的坑。原因很简单刷新后vuex重置了动态路由丢失用户访问的当前路径暂时匹配不上任何路由。解决办法通常是两级路由保底——在守卫里用to.matched.length 0作为兜底条件先把路由重新挂载然后用next({ ...to, replace: true })重新进入一次。2.4 第四层按钮级权限不能只靠隐藏后端校验才是底线按钮级权限一般用自定义指令实现。Element Admin生态里常见的做法是注册一个v-permission指令传入需要的权限码数组如果没有权限就直接把DOM节点移除防止用户看到按钮。const permission { mounted(el, binding) { const { value } binding const storeUserPermissions store.getters.permissions const hasPermission storeUserPermissions.some(p value.includes(p)) if (!hasPermission) { el.parentNode el.parentNode.removeChild(el) } } }但我在实际项目里一直强调一个原则前端按钮隐藏只是用户体验问题真正的权限控制必须在后端校验。因为前端的任何控制都可以被绕过恶意用户直接调接口就能操作。所以按钮维度控制的是交互入口接口维度的越权校验才是安全底线。3. CMS模块与权限体系结合的底层设计思路CMS管理系统接进来之后系统就不再只是一个“管理后台壳子”了它开始承担真正的内容生产业务。这一块我建议按“内容模型-分类树-状态机-审核流”四步走每一步都和权限体系发生关系。3.1 内容模型用单表还是分表取决于内容类型是否固定如果系统里只有“文章”一种内容那设计一张article表就够了。如果打算后面支持“文章、图集、视频、下载资源”多种内容就不要硬塞进一张表而是设计一张统一的内容主表加上扩展属性表。以文章表为例核心字段包括id主键title标题category_id所属分类author_id作者cover封面图URLcontent正文内容status状态码audit_user审核人audit_time审核时间publish_time发布时间create_time/update_time创建和更新时间一个比较容易被忽略的字段是status它既是内容状态又是审核流程的判断依据。我习惯用整数状态码来标记而不是直接存字符串因为整数在查询索引和前后端交互上都更高效。3.2 分类树的两种实现方式CMS里几乎都离不开分类。分类通常有两种形态一种是固定层级的树形结构一种是无层级的标签式结构。对于文章类CMS我建议用树形结构因为分类天然有父子关系比如“公司动态”下面可能有“产品发布”、“团队活动”这种子分类。树形结构在数据库里最简单的存法是parent_id自关联查询时一次性取出全部分类在Java或者JavaScript层组装成树。不推荐在SQL里反复递归查询因为分类数据量不会特别大一次性取出后在内存里组装反而更快。前端用Element UI的el-tree组件渲染分类树支持选中和勾选两种模式。这里有个小经验如果只是单选文章所属分类用node-click事件如果需要批量操作比如按分类批量审核才需要开启show-checkbox支持多选。3.3 文章状态机草稿、待审核、已发布、已下线CMS的状态设计没有统一标准但至少要覆盖内容从创建到生命周期结束的几个基本阶段。我通常会用下面这个状态流转草稿(0) - 待审核(1) - 已发布(2) - 已下线(3) \ / - 驳回(4)每个状态的触发动作不同编辑可以创建草稿、提交审核管理员或审核人可以把待审核的文章变为已发布或驳回发布后的文章可以手动下线也可以设置定时自动下线。状态字段加上审核人、审核时间之后整个内容的审计链路就完整了。这块对于做运营的后台非常关键不然出了问题根本不知道是哪一步、哪个人弄的。3.4 权限与控制流怎么绑在一起CMS模块的权限不再只是“能不能看见菜单”而是要细化到“能不能审核”、“能不能发布”。我的做法是利用权限码来做流程控制编辑角色拥有权限码cms:article:add、cms:article:edit、cms:article:submit审核角色拥有权限码cms:article:audit、cms:article:reject管理员拥有全部权限外加cms:article:delete、cms:article:offline前端在操作按钮上绑好这些权限码后端在接口上做好角色校验双管齐下。这样一个编辑账号就算绕过前端拿到了审核接口地址后端也会拒绝因为他没有对应的角色和权限码。4. 从Demo跑通到业务上线绕不开的五个实际问题模板项目跑起来很容易npm install加npm run dev两分钟就能看到一个完整的后台界面。但从Demo到真正上线中间踩过的坑能写满一屏。我只挑自己项目中真实遇到、并且花了不少时间解决的问题来说每一个都是典型的“不看文档就会掉进去”的坑。4.1 菜单数据是从后端返回还是前端写死很多模板项目会把菜单直接写在路由配置里然后用角色去过滤。这种方式在系统菜单固定时没问题但如果菜单要支持动态配置运营可以在后台新增一个导航入口那就必须用后端动态返回的菜单数据。我的最终方案是默认菜单写在前端作为静态路由扩展菜单存后端作为动态路由。这两种路由在守卫里合并先加载静态再加上动态。这样系统的核心页面不会被后台配置搞挂扩展页面又保持了灵活性。4.2 axios请求封装不是简单封装get和postElement Admin模板一般自带axios封装但很多封装只是做了基本的拦截。实际项目中至少需要在这些方面做增强统一错误处理HTTP 401跳登录页403提示无权限500弹后端返回的错误消息。业务错误码处理后端接口经常返回{ code: 200, data: ... }这种结构code不等于200时也要进入错误分支。重复请求屏蔽针对提交类的接口在Pending期间禁止再次点击防止表单重复提交。取消失效请求在路由切换时取消之前未完成的请求节省连接资源。重复提交这个问题很多项目到上线之后才暴露出来。用户双击提交按钮结果创建了两条一模一样的文章记录。解决方式也很简单封装一个requestLock同一个接口同一个入参在短时间内只允许发出一次。4.3 按钮权限码的维护和组织方式权限码最容易乱在命名上。有人用中文备注有人用随机字符串有人直接不建表写死在代码里和后端各自为政。到最后权限码对不上前端判断失效后端也不认。我自己的习惯是统一按模块:子模块:操作的三段式命名比如cms:article:addcms:article:editcms:article:auditsystem:user:resetPwd这段字符串同时作为前端指令参数和后端接口校验标识。建议在项目初期就维护一份权限码清单表前后端共用谁改谁同步不然后面排查起来真的很痛苦。4.4 刷新页面之后动态路由丢失的完整解法这个问题在动态路由方案里属于必踩的坑。页面一按F5vuex数据清空动态addRoute加进去的路由也全部消失。可以利用路由守卫循环等待路由就绪的方式也可以把静态路由和动态路由结合处理。比较稳定的一套解法分三步在路由守卫里判断用户是否已登录已登录但vuex中没有权限数据时先拉取权限数据并addRoute然后next({ ...to, replace: true })因为next重新进入了目标路由此时to.matched已经存在不会匹配到404。首页白屏的问题基本能解决但要注意动态路由必须加在静态通配符路由/:pathMatch(.*)*之前。4.5 CMS富文本编辑器与内容的XSS过滤CMS模块离不开富文本编辑器推荐用wangEditor或者Quill这类成熟方案。但富文本编辑器的核心风险在于XSS用户可以在内容区插入恶意脚本如果原样存储、原样渲染问题会很严重。后端必须对富文本内容做白名单过滤只允许p、img、a、ul、ol、li、strong、em、h2-h4这类安全标签。属性方面建议只保留src、alt、href、title这几个常见属性。图片上传时也要校验文件类型和大小防止恶意脚本伪装成图片上传。个人强烈建议不要在纯前端层面过滤因为绕过前端请求直接POST后端接口前端的任何过滤都不生效。完成后端过滤并且对输出到页面的内容做一次兜底转义双重保险。5. 数据权限和角色设计里的几个容易忽视的边界很多项目把“权限管理”理解成“菜单权限”实现了菜单和按钮的控制就觉得权限系统完成了。但真实业务中数据范围的权限往往更重要甚至更能体现出系统的能力。5.1 数据范围编辑只能看到自己写的文章CMS里很典型的一个需求普通编辑只能看到自己创建的文章主编能看到整个栏目的文章管理员能看到所有部门的内容。这就是数据权限不是菜单和按钮能解决的。实现上通常做法是SQL层面拼接数据范围条件。角色表里加一个data_scope字段枚举值含义拼接条件1仅本人数据author_id 当前用户2本部门数据dept_id in 当前用户所属部门及子部门3全部数据不加条件这个字段的存在会让查询方法看起来没那么干净但它是控制数据越权最直接的手段。我曾经在一个项目里漏掉了数据权限结果运营在后台看到并修改了其他城市同事的文章非常尴尬。5.2 超级管理员不要走常规权限判断通常开发同学都会给“超级管理员”一个通配符角色对应权限码*:*:*判断时如果角色是超管就直接放行。这个设计问题不大但要注意别在业务层写死if (username admin)这种判断正确做法是在底层权限框架里统一处理业务代码不感知角色差异。这样做的目的是保证权限策略可扩展以后新加一个“运营总监”角色即使也拥有全部权限也不需要改代码。5.3 权限变更后的实时生效策略一个系统上线后运维给某个人改了角色、或者调整了某个菜单的权限用户那边需要什么操作才能生效有些系统的做法是用户重新登录。在权限要求不是特别严苛的场景下这也可以接受但体验确实差了一些。更友好的做法是在后端支持“踢下线”功能权限变更时通过WebSocket或者下一次请求时让用户强制刷新权限缓存。这个功能不一定放在第一版但架构上要留好口子至少把获取权限的接口独立出来方便后续平滑升级。6. 部署上线前的检查清单与性能优化开发完毕并不代表可以上线。我每次负责后台项目上线前都会按一套固定清单过一遍免得在关键时刻掉链子。6.1 接口访问日志与操作审计后台系统是核心业务系统谁在什么时间干了什么操作必须能查。除了登录日志还要有关键操作的操作日志。日志不需要全量记录所有读接口但写操作必须记录。最简单的方案是后端AOP切面给需要审计的接口打上OperationLog注解切面里记录操作人、操作类型、参数、IP、耗时、结果。CMS的发布、下线、删除操作尤其要记不然出问题没有回溯手段。6.2 前端构建体积优化Element Admin带上Element UI完整组件库打包体积极容易膨胀。做构建优化时有几个直接见效的手段开启组件按需引入用babel-plugin-component把用不到的表单、日期选择器、弹窗组件全部摇掉路由懒加载每个页面单独分包而不是把全部页面打进一个chunk里关闭源码映射生产环境的productionSourceMap设为false体积能小很多开启Gzip配合Nginx的gzip_static对整体传输体积的优化非常明显。经验来看做完这几步后台首页从首次打开三四秒能压到一秒半左右感官提升还是很明显的。6.3 部署时的环境变量管理环境变量别写在代码里。Element Admin配套的Vue.config.js里通常会区分development、test、production三个环境API地址、上传地址这些按环境取值。有一点容易踩坑的是前端在构建时把环境变量打进了静态资源里所以换环境必须重新构建不能直接把构建好的dist包拿到另一个环境部署。如果要做多环境共用同一个包就需要运行时挂载全局配置可以配合Nginx注入或使用env.js动态加载方式。6.4 Nginx需要单独处理的前端路由问题使用History模式路由时Nginx必须配置try_files回退到index.html否则直接访问二级路径会报404location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }另外如果前端静态资源请求用的是相对路径在部署到子目录时需要设置base和publicPath否则CSS和JS全部加载失败。这种问题通常只在部署当天才暴露提前check一下能省很多时间。7. 从我自己的项目经验出发最后想说的几件事这个组合方案我先后在三个不同类型的项目里落地过一个偏企业信息门户、一个偏内容社区、还有一个是内部管理工具效果都比较稳定。ElementAdmin作为底子权限系统提供访问控制CMS模块负责内容生产两者共用一个后台数据和用户体系天然统一。有一点我体会最深权限和CMS从来不是两个孤立的功能模块它们的耦合点也绝不只是侧边栏菜单而是贯穿了用户、角色、内容、状态、审核、审计的全流程。设计时如果分开做后面接起来必然要返工。如果你正准备用这个方案启动自己的项目我的建议很直接第一版不要贪多先把用户登录、动态菜单、文章发布、状态流转、操作日志这几个主链路跑通权限码和数据权限的模型想清楚再动手写。过程里遇到问题不要急着怀疑模板不行大部分问题都是动态路由时序、权限数据同步和状态管理这三件事没有理顺导致的。按照上文梳理的链路一步一个脚印走你会发现自己搭出来的后台既干净又扛得住需求变化这大概就是这个方案最让人省心的地方。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价