
简介这是一套面向Java与前端开发者的内容管理系统CMS实战项目资源适用于SpringBoot与Vue技术栈的中高级学习者及全栈工程师旨在解决传统单体CMS开发效率低、维护难、前后端耦合深等痛点。资源采用SpringBoot3构建后端RESTful API集成用户认证、内容管理、评论系统等核心模块前端基于Vue2实现组件化界面结合UEditor富文本编辑器及响应式布局支持多终端访问。压缩包共2000个文件含1415个Java业务与配置类、226个Vue组件、179个JS交互逻辑、87个properties配置文件及配套CSS样式资源整体大小17.07MB结构清晰、模块解耦度高便于分层学习与二次开发。目前已有27人下载学习资源附带完整前后端工程结构、主流中间件集成示例及生产级部署说明可直接用于课程设计、毕设开发或企业内部CMS原型搭建。 从拿到这个项目压缩包的名字开始我大概就猜到里面是什么了——一套围绕内容生产、审核、发布、管理全流程的业务系统技术上选了SpringBoot3做后端服务Vue2做前端界面中间走的是标准的前后端分离模式。说实话在我接手过的CMS类项目里这个组合不算最新潮但绝对算得上成熟稳定既能扛住实际业务又不会因为技术选型太激进导致团队上手困难。如果你正在准备做一套内容管理系统或者想找一个能直接落地的前后端分离实战项目来参考这套代码里值得琢磨的东西不少。我之所以对这类项目特别有感触是因为内容管理系统本身是个很“标准”的业务系统有用户、有角色、有权限、有内容发布和审核、有文件上传管理几乎把后端常见的模块都覆盖到了。而SpringBoot3 Vue2这个组合又刚好处在Java生态更新换代、前端技术栈新旧交替的节点上处理得好你能同时摸清两条技术线的思路处理不好光环境配置和版本兼容就能让你折腾一星期。这篇文章我把这套系统的设计思路、核心实现、部署细节以及我实际踩过的坑都梳理一遍争取让拿到项目的人能少走弯路。1. 项目整体设计与技术选型的思考1.1 为什么后端会选择SpringBoot3SpringBoot3是2022年底正式发布的大版本最核心的变化是强制基于Spring Framework 6和Jakarta EE 9基础JDK版本起步就是17。这意味着它默认站在了长期支持版本的肩膀上无论是ZGC垃圾回收器、虚拟线程Java 21配合使用还是更强的安全特性都能够直接用上。对于内容管理系统这种追求稳定和并发的业务场景选SpringBoot3意味着你不需要再为老版本的兼容补丁操心项目一开始就站在了一个相对干净的基础环境上。当然选SpringBoot3不只是为了“新”更关键的是它的自动配置机制和生态整合能力。在CMS项目里我们需要的核心依赖无非是SpringMVC处理请求、Spring Security做认证授权、MyBatis或JPA操作数据库、Redis做会话和缓存。这些组件在SpringBoot3下都有对应的starter引入依赖后在配置文件里写几行参数就能跑起来省去了大量XML配置的麻烦。我实际开发中感受最深的是SpringBoot3对于配置文件绑定的校验更严格了数据源或者Redis连接配置写错启动阶段就会立刻报错而不是等到运行期才出问题这对排除故障是实打实的帮助。还要提一点SpringBoot3内置的Web服务器也做了升级Tomcat 10 / Jetty 11 / Undertow 2.3都换到了Jakarta命名空间如果你以前处理过从javax迁移到jakarta的工作就有体会SpringBoot3帮你把这些底层差异都封装掉了写业务代码时基本感知不到这些切换。1.2 为什么前端仍然坚持Vue2看到Vue2有些朋友可能会问Vue3都出来这么久了怎么还有新项目用Vue2这个话题我们得客观看待。内容管理系统虽然是新项目但很多团队手上已经积累了大量基于Vue2的组件库、后台模板和业务封装比如Element UI、vue-element-admin这类经典搭配如果全部推到Vue3重写成本和风险都是翻倍的。SpringBoot3已经在后端跨了一大步前端保持Vue2反而更容易保证项目的可控性。另外就CMS业务本身来说Vue2的响应式原理、组件通信、路由和状态管理已经完全够用。内容管理系统里面虽然功能多但绝大部分是列表、表单、弹窗、详情页这样的常规交互Vue2的options API在这种场景下属实够用。而且Vue2生态经过多年沉淀踩坑资料非常丰富无论遇到什么问题都能查到解决方案。从团队招聘、成员上手、项目维护的角度看Vue2依然是一个务实的选择。不过这里我也想给个诚恳建议如果你接下来接手这个项目前端部分的代码风格尽量保持Vue2规范的写法就行但如果未来有升级计划可以提前在代码里把Composition API相关的逻辑隔离出来减少后期迁移的成本。1.3 前后端分离的核心价值这套系统最大的特点就是前后端完全分离后端只提供RESTful API接口前端通过HTTP调用接口拿到JSON数据自行渲染。两者通过约定好的接口文档进行协作不存在服务端渲染那种页面混在一起的情况。前后端分离带来的直接好处有三个。第一前端和后端可以并行开发后端定义好接口路径和返回数据结构后前端直接用Mock数据模拟联调不用相互等待。第二同一个后端接口可以同时支撑PC后台、移动端H5甚至小程序只要前端重新做适配即可内容管理系统的接口完全可以复用给前台门户的多个端。第三前后端可以独立部署后端只需要对内网开放接口前端通过Nginx反向代理或网关转发整个系统的扩展性和安全性都更好。当然前后端分离也不是没有代价最直观的就是跨域处理和Token鉴权这也是很多新手最容易卡住的地方后面我会展开讲。2. 后端架构设计与核心实现2.1 后端项目结构划分这套系统的后端采用经典的分层架构从上到下依次是Controller层、Service层、Mapper层外加一个独立的config包来放各种配置类common包放统一返回结果和异常处理entity或domain包放数据库实体。以我见过的大量CMS项目为参照比较清晰的结构大概是这样com.example.cms ├── controller # 接收前端请求参数校验调用service │ ├── AdminUserController.java │ ├── ContentController.java │ ├── CategoryController.java │ ├── FileController.java │ └── AuthController.java ├── service # 业务逻辑层 │ ├── impl │ │ ├── UserServiceImpl.java │ │ ├── ContentServiceImpl.java │ │ └── ... ├── mapper # 数据访问层配合MyBatis-Plus │ ├── UserMapper.java │ ├── ContentMapper.java ├── entity # 数据库实体 ├── dto # 前端传参和数据返回对象 ├── config # Spring配置如SecurityConfig、RedisConfig、Knife4jConfig ├── common # 统一返回Result、全局异常处理器 └── CmsApplication.java # 启动类这种结构的核心思想是各层职责单一Controller不写业务逻辑Service不直接写SQL操作。实际开发中我见过不少项目为了让代码显得“简洁”在Controller里堆了一大坨业务代码结果到了后期改一个需求连测试都找不到入口。该分层的地方必须分层这是保证后端代码可维护的基本盘。2.2 认证与权限设计CMS系统涉及后台管理接口不能裸奔认证和权限是第一道关卡。这套项目里常见的方案是Spring Security JWT用Redis存储登录态和权限信息兼顾分布式扩展能力和无状态鉴权。登录流程是这样的用户提交账号密码到/login接口后端校验用户名密码密码用BCrypt加密存储校验通过后生成一个JWT token返回给前端。前端把token存在localStorage或cookie里之后每次请求都在请求头里带上Authorization: Bearer token。后端加一个JWT过滤器在Spring Security的过滤器链里拦截请求校验token有效后解析出用户ID和权限列表。这里有一个容易踩的坑JWT本身是无状态的token一旦签发在有效期内是无法主动作废的。如果用户在前端点了退出登录后端只能通过把token加入黑名单或者依赖Redis里的session来做到“伪退出”。我在这套项目里推荐的做法是token签发生成后把它的jti唯一标识存入Redis设置过期时间和token有效期一致每次请求时先检查Redis是否存在这个jti如果不存在说明已经登出或失效直接拦截掉。这样兼顾了JWT的无状态优势和主动失效的需求。Spring Security 6对应SpringBoot3的配置方式和老版本差异比较大主要是WebSecurityConfigurerAdapter已经废弃了你需要用SecurityFilterChain的Bean来配置过滤链。这是我实际升级时被卡住好久的地方如果用了旧版本的教程会报一堆方法找不到的错误。2.3 内容管理核心模块设计内容管理的核心模块一般围绕“栏目-文章”这个模型展开。文章挂在栏目下面栏目有层级结构。数据库层面至少需要两张表栏目表category和内容表content内容表通过category_id关联栏目。内容表的设计要考虑到CMS的特殊需求除了基本的title、content、author字段外还得有status字段做审核状态管理。在我见过的CMS项目里status一般用数字枚举来表示0草稿、1待审核、2已发布、3已驳回、4已下线。这种状态设计比简单的“发布/未发布”灵活很多运维人员可以管理完整的生命周期。内容列表页的查询往往需要多条件组合比如按标题模糊搜索、按栏目精确筛选、按发布时间范围筛选、按状态筛选。这里用MyBatis-Plus的LambdaQueryWrapper可以很优雅地实现LambdaQueryWrapperContent wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(title), Content::getTitle, title) .eq(categoryId ! null, Content::getCategoryId, categoryId) .eq(status ! null, Content::getStatus, status) .between(startTime ! null endTime ! null, Content::getPublishTime, startTime, endTime) .orderByDesc(Content::getUpdateTime); PageContent page contentMapper.selectPage(new Page(pageNum, pageSize), wrapper);这套写法有个明显的好处条件为false时对应条件不会拼接到SQL里完全避免了手动拼接SQL语句时可能出现的空指针或多余WHERE的情况。2.4 文件上传与资源管理内容管理系统基本离不开图片、附件、视频等资源管理后端需要一个统一的文件上传接口。SpringBoot处理文件上传比较简单利用MultipartFile接收文件然后把文件存储到服务器本地指定目录或者如果业务规模大了就接OSS对象存储。这套项目里如果做本地存储需要注意几点存储路径不要放在项目内部否则重新部署会导致文件丢失应该存到独立目录或者云存储。文件访问需要单独映射不要把整个静态资源目录无差别暴露。上传文件必须做类型和大小的校验避免上传恶意文件。只允许白名单后缀比如jpg、png、gif、pdf、doc、docx、xls、xlsx、zip等文件大小根据业务控制在5MB到50MB之间。文件名要重命名防止中文乱码和重复可以用UUID或时间戳处理。文件下载和预览也是常见需求Word、PDF这些文档类的预览一种思路是后端把文件转成PDF前端用iframe直接展示另一种是后端提供文件流接口前端用插件加载。Electron那种桌面方案在后台系统里不实用一般还是iframe PDF插件比较稳。3. 前端架构与功能实现3.1 Vue2项目结构划分前端项目基于Vue2 Element UI Vuex Vue Router典型的后台管理模板结构。我在看这套项目时比较关注目录是否清晰、公共组件是否抽得合理。一个良好的前端结构大致是这样的src ├── api # 接口请求定义按业务模块拆文件 │ ├── login.js │ ├── content.js │ ├── category.js │ └── user.js ├── assets # 静态资源图片、全局样式 ├── components # 公共组件 │ ├── Pagination │ ├── UploadImage │ ├── RichEditor │ └── ... ├── router # 路由配置带动态路由和权限控制 ├── store # Vuex状态管理 │ ├── modules │ │ ├── user.js │ │ └── app.js ├── views # 页面组件 │ ├── login │ ├── content │ ├── category │ ├── system │ └── dashboard ├── utils # 工具函数request封装、auth校验 ├── App.vue └── main.jsapi目录按模块拆分是我特别推荐的做法一个页面一个api文件维护起来非常清晰。utils目录下的request.js是Axios实例的二次封装统一处理baseURL、请求头Token注入、响应拦截器等。3.2 核心页面与组件内容管理系统的前端核心页面主要有这么几个登录页、工作台Dashboard、内容列表页支持多条件筛选、分页、批量操作、内容编辑页富文本编辑器、栏目管理页树形结构、用户管理页、角色权限配置页、系统设置页。内容列表页是所有页面里最关键的也是后面各种功能扩展的载体。这个页面通常包含筛选区标题输入框、栏目下拉选择、状态下拉选择、时间范围选择、表格区勾选列、标题、栏目、作者、状态、发布时间、操作按钮、分页区。我在实操中会特别关注表格的loading状态和分页逻辑Element UI的el-table配合v-loading指令体验很不错。内容编辑页一般用路由传参router.push({ path: /content/edit, query: { id: row.id } })进入编辑页后根据id调接口回显数据。如果没有id就是新建状态。保存时要注意区分“存草稿”和“提交审核”两种操作这两种操作在后端对应的status不同前端要用不同的按钮和确认逻辑来处理。组件层面富文本编辑器是CMS的标配功能。Vue2生态里Tinymce和Quill是两款主流选择。Tinymce功能全面支持图片上传、表格、代码块等适合做功能复杂的编辑器Quill更轻量界面简洁。这套项目如果用的是Tinymce需要注意图片上传需要自定义images_upload_handler。如果用的是Quill则关注图片上传需要自定义toolbar处理。代码我就不贴了这部分网上示例太多了重点是理解它的上传回调机制。3.3 富文本编辑与发布富文本编辑器的接入是CMS系统开发中比较麻烦的一环。常见的坑是图片上传逻辑富文本内的图片是通过编辑器上传并返回URL还是以base64形式直接嵌入内容这两种方案我更推荐前一种——base64会导致数据库中的content字段变得无比巨大列表查询性能和存储空间都会拖垮。实操中可以把上传接口封装成一个Promise在编辑器配置里指定上传处理函数images_upload_handler(blobInfo, success, failure) { const formData new FormData() formData.append(file, blobInfo.blob()) uploadFile(formData).then(res { if (res.code 200) { success(res.data.url) } else { failure(res.message) } }) }这里面有个细节上传接口的返回格式要和你封装的统一返回结果保持一致否则编辑器拿到URL之前要做额外处理。还有一种情况是富文本内容保存时前后端没有处理好XSS防护比如用户通过编辑器插入一段