ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue论坛系统实战:前后端分离与MyBatis+MySQL架构解析

SpringBoot+Vue论坛系统实战:前后端分离与MyBatis+MySQL架构解析 做论坛网站管理系统市面上能拿到的开源项目确实不少但大多数要么只有前端页面、后端逻辑一团乱要么就是老掉牙的JSP加Servlet那一套部署起来相当头疼。这次分享的这个基于SpringBoot Vue的论坛系统源码是我在2025年初完整走了一遍从数据库设计、后端接口开发再到前端页面联调的项目前后端分离MyBatis负责持久层MySQL做数据存储整体结构很清晰直接拿来改一改就能做毕业设计、个人作品集或者当作企业内网的知识社区底子来用。我尽量把拆解思路、关键配置、踩过的坑都写出来不管你是刚接触框架的初学者还是准备接项目的老手应该都能从中拿到一些实在的东西。1. 项目整体设计与技术选型思路1.1 为什么选择前后端分离的SpringBoot Vue方案在做论坛类系统之前需要先想清楚一个问题这个项目是给谁用、规模有多大、后续要不要扩展。如果只是做个单机版的管理后台用Thymeleaf套页面也不是不行但一旦涉及到多端复用比如以后要做小程序、App或者需要多人协作开发前后端那前后端分离就是必须走的路。SpringBoot在后端接管了大部分繁琐的配置内嵌Tomcat打成一个jar包就能跑Vue在前端用组件化思维拆页面开发调试速度快生态也比较成熟。这两者搭配起来前端只管渲染和交互后端只管接口和数据职责边界非常清楚。我在设计这个论坛系统时特意把后端按“表现层、业务层、持久层”拆开前端按“视图组件、路由、状态管理、接口请求”拆开。这样做的直接好处是某个页面出了问题你不需要去后端翻一遍代码看接口返回的JSON结构就能定位某段业务逻辑要改也不会影响页面上其他功能。对于论坛这类典型的CRUD加用户认证的项目来说这种做法虽然前期会多一点结构设计的工作但后续维护成本明显低很多。1.2 持久层和数据存储MyBatis加MySQL的组合逻辑很多人纠结用JPA还是MyBatis我的观点是CRUD简单场景两者差不多但论坛系统的帖子列表、评论树、用户权限这类查询经常会有多表关联、条件动态拼接的需求MyBatis的SQL可控性明显更占优势。SQL写出来是什么样子执行就是什么样子心里有底一旦遇到慢查询把日志里那条SQL拿出来直接在数据库客户端跑一遍就能分析。再加上论坛系统的业务相对固定不太需要JPA那种实体自动映射的便利性所以MyBatis是更适合当下的选择。MySQL这边选用8.0版本主要考虑的是字符集默认就是utf8mb4不需要像5.7那样还要手动去改配置。存储表情符号或者生僻字的时候utf8mb4是必须的论坛里用户发个emoji如果字符集不对直接乱码或者插入失败这种问题排查起来很烦。另外在MySQL 8.0下JDBC驱动的连接参数也要注意serverTimezone必须显式指定否则插入时间字段会出现时区偏移。1.3 论坛系统的核心功能模块拆解论坛系统看起来简单但把需求列出来其实并不少。我这个项目拆成了五大块用户模块注册、登录、个人信息修改、板块模块分类管理、板块列表、帖子模块发布、编辑、删除、分页列表、帖子详情、评论模块一级评论加回复、评论列表、管理后台模块用户管理、帖子审核、数据统计。每一块都是独立的Controller、Service、Mapper模块之间通过服务接口相互调用不直接操作对方的数据表。这种模块化的意义在于你可以先跑通“发帖、看帖、评论”这条主链路再慢慢填充管理后台的功能不会出现牵一发动全身的局面。实际上我在开发排序的时候也是按这个顺序来的先把用户认证打通再做帖子主流程最后才补管理后台的统计和审核。分阶段交付还有个附带的好处就是你每完成一个阶段就能跑起来自测一遍问题能更早暴露出来。2. SpringBoot后端从入口到数据落地的完整链路2.1 工程目录结构与分层规范一个干净的后端工程目录能帮你省掉很多找文件的烦恼。我的项目里包名统一放在com.example.forum下面职责划分为controller、service、mapper、entity实体类、dto数据传输对象、common通用返回类、异常处理、工具类、config配置类这几层。Entity只负责映射数据库表结构不往外透传Controller只接收参数、调用Service、返回统一结果Service里写业务逻辑比如发帖之前先检查用户是否被禁言评论之前判断帖子是否存在Mapper就是纯粹的SQL接口。分层的规则定下来之后最重要的是“谁不能调用谁”。比如Controller层不应该直接注入Mapper所有的数据访问都要经过Service。这样听起来好像多绕了一层但当你要加缓存、加事务、加权限校验的时候只要在Service层加注解或代码就能覆盖到所有入口不需要在Controller里到处补。我在实际写的时候测试阶段确实觉得有时候一步到位写Controller更快但后期加功能时才发现分层的好处比如统计用户发帖数我只要在一个Service方法里聚合前端所有需要这个数据的地方都通用了。2.2 用户认证与权限控制的落地方式论坛系统的权限控制我不推荐用Session那套而是用JWT。发帖、点赞、评论这些操作需要识别用户身份管理员还需要判断有没有删除的权限。JWT的方案是用户登录成功后后端生成一个带过期时间的token返给前端前端后续每个请求都在HTTP头里带上Authorization: Bearer token后端通过拦截器解析token把用户信息放到ThreadLocal中方便Service层随时取用。具体实现在SpringBoot里就是写一个JwtInterceptor实现HandlerInterceptor接口在preHandle里校验请求头白名单请求比如登录接口、首页帖子列表直接放行。然后注册这个拦截器到WebMvcConfigurer中指定拦截路径是/api/**排除登录注册接口。token签发我用的是io.jsonwebtoken:jjwt这个库密钥配置放在application.yml里过期时间设成24小时。这里有个我踩过的坑JWT的subject不要存复杂的对象只存用户ID需要用户信息时再通过ID去数据库查否则token体积膨胀而且用户改了头像昵称之后token里存的旧信息会导致前端展示不更新。权限这块我做了简单的角色区分普通用户和管理员。管理员接口通过自定义注解RequireAdmin配合拦截器实现。拦截器解析token拿到用户ID后查一下角色类型如果不是管理员直接返回403。这种方式比在Controller里到处写if (user.getRole() ! 1)要优雅得多新增一个管理员专属接口时只要在方法上标个注解就行。Component public class JwtInterceptor implements HandlerInterceptor { Autowired private StringRedisTemplate stringRedisTemplate; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String authHeader request.getHeader(Authorization); if (authHeader null || !authHeader.startsWith(Bearer )) { throw new BusinessException(401, 请先登录); } String token authHeader.substring(7); // 解析token获取userId Integer userId JwtUtil.parseToken(token); if (userId null) { throw new BusinessException(401, 登录状态已过期); } UserContext.setUserId(userId); return true; } Override public void afterCompletion(...) { UserContext.clear(); } }2.3 核心数据表设计与索引优化思路论坛系统的数据库表不需要设计得多复杂但几张核心表的关联关系要想清楚。用户表user、板块表category、帖子表post、评论表comment是四张主力表。我设计的时候遵循了几个原则主键统一用自增ID不用UUID做主键原因是自增ID在索引写入和分页查询时性能更好所有表都加上create_time和update_time论坛这种系统特别需要按时间排序外键不建物理外键只在逻辑上通过user_id关联因为物理外键在插入大量数据时会拖慢性能而且后续要分表也麻烦。帖子表的关键字段包括category_id所属板块、user_id发帖人、title、content富文本内容、view_count、like_count、comment_count、status正常/删除/待审核。这里要注意的是content字段用TEXT类型就好不要用VARCHAR因为帖子内容可能很长。评论表我用了一个parent_id字段来表示回复关系为0的是顶层评论非0的表示回复某条评论。这种设计查询时需要通过parent_id关联回帖列表要用两次查询组装成树形结构但胜在逻辑简单、写入方便。索引方面我建了这些post表的create_time索引用于首页按时间排序category_id索引用于按板块过滤comment表的post_id索引用于查某个帖子下的评论。单表数据量在几十万级别时这些索引完全够用。要注意的是不要在content这种大字段上建索引页面上也不要做全表LIKE %关键词%的搜索如果确实需要搜索功能要么用MySQL的全文索引要么接Elasticsearch不然后期数据多了会扫全表。我在开发初期图省事用LIKE做了一次内容搜索测试时数据量小没感觉后来导入了两万条测试数据搜索一次要两秒多果断把搜索改成了基于标题和摘要的LIKE匹配勉强够用。3. MyBatis持久层动态SQL与性能优化3.1 Mapper接口与XML映射的配合方式使用MyBatis时我采用接口加XML的经典组合。Mapper接口里只声明方法XML文件里写SQL。这样SQL语句和Java代码分离改动SQL时不需要重新编译Java类在开发阶段很有用。一个比较关键的配置项是map-underscore-to-camel-case: true加上这个之后数据库表中的create_time字段就能自动映射到实体类里的createTime属性不用手动写繁琐的映射关系。XML文件放在resources/mapper目录下在application.yml里通过mybatis.mapper-locations: classpath:mapper/*.xml告诉MyBatis去哪里找。另外需要配置mybatis.type-aliases-package这样XML里的resultType可以直接写实体类名不用写全限定名。这些配置虽然小但漏掉任何一个都会遇到莫名其妙的报错——最常见的就是Invalid bound statement (not found)通常是XML文件没被扫描到或者方法ID对不上。3.2 动态SQL处理论坛的高频查询场景论动态SQL的核心价值论坛系统的帖子列表是最好的例子。首页帖子列表要根据不同的条件组合过滤可能按板块筛选、按关键词搜索、按最新或最热排序还可能按状态过滤。如果不使用动态SQL你需要写三四个不同的Mapper方法参数组合一多就直接爆炸。用where标签加if判断一个方法就能搞定。select idselectPostPage resultTypecom.example.forum.entity.Post SELECT p.*, u.username, u.avatar, c.name AS categoryName FROM post p LEFT JOIN user u ON p.user_id u.id LEFT JOIN category c ON p.category_id c.id where if testcategoryId ! null AND p.category_id #{categoryId} /if if testkeyword ! null and keyword ! AND p.title LIKE CONCAT(%, #{keyword}, %) /if if teststatus ! null AND p.status #{status} /if /where choose when testorderBy hot ORDER BY p.comment_count DESC, p.create_time DESC /when otherwise ORDER BY p.create_time DESC /otherwise /choose /select这里一个比较容易忽略的细节是CONCAT(%, #{keyword}, %)。直接用%${keyword}%可能遇到SQL注入即便只是内部项目也不能有这种安全问题。用#{}参数占位符MyBatis会把它处理成预编译语句这样是安全的。另外一个细节是LEFT JOIN关联用户表把用户名和头像直接查出来避免在Service层再循环查数据库不然N1问题会让首页接口慢到不可用。3.3 事务与缓存保证评论和帖子的数据一致性论坛系统里有一个很容易踩的坑发帖时需要先在post表插入数据再更新category表里的帖子总数如果两步之间出了异常帖子数据就会出现不一致。解决办法是在Service方法上使用Transactional注解让两步操作在一个数据库事务里完成要么都成功要么都回滚。SpringBoot里默认事务是只回滚RuntimeException如果业务里抛了受检异常需要手动在注解里指定rollbackFor Exception.class否则事务会静默失效。MyBatis的二级缓存在我这个论坛项目里并没有启用。原因很简单论坛的帖子数据、评论数据是典型的写多读多场景缓存失效的频率很高命中率反而上不去。我的做法是把热点数据比如首页的帖子列表前几页缓存到Redis里设置5分钟过期如果后台有帖子更新主动删除对应的缓存Key。这样实现方式灵活又能精确控制缓存粒度。直接用MyBatis二级缓存需要序列化实体类而且一旦涉及多表关联查询缓存的数据可能过期不准确性价比不高。4. Vue前端从零搭建论坛界面与交互逻辑4.1 工程初始化与目录组织前端部分我选Vue 3加Vite。相比Vue 2时代的WebpackVite的启动速度快了一个量级开发体验也好很多。初始化项目用npm create vuelatest它会生成一个带有Vue Router和Pinia的基础模板。目录组织上src/api放所有接口请求src/router放路由配置src/views放页面组件src/components放通用组件src/store放Pinia状态。如果之前用过Vue 2要注意Vue 3的API风格更适合用组合式API页面里按功能组织逻辑而不是按选项组织。比如发帖页面里表单校验逻辑放一起、提交逻辑放一起、图片上传逻辑放一起这样比data、methods、computed各写一块更清晰。刚开始可能不习惯但写过两个页面之后你会感受到改动某个功能时才不用跳来跳去。4.2 Axios统一封装与路由守卫的前端权限控制前端的接口请求不能一个个裸写axios.get必须统一封装。我在src/api/request.js里创建了一个Axios实例设置baseURL为/api然后在请求拦截器中从Pinia里取出token添加到Authorization头。响应拦截器里做统一错误处理比如后端返回401时自动跳回登录页并且清除本地登录状态。这样一来每个页面的代码只需要专注于业务本身请求失败时统一弹出消息提示代码量少很多。路由守卫是前端权限控制的第一道关卡。我定了两个路由/是用户首页/admin下面是管理后台。在router.beforeEach里判断用户是否登录以及是否访问了超出角色的路由。比如未登录用户直接访问发布页面就重定向到登录页普通用户访问/admin就提示无权限。后端接口还有二次校验所以前端路由守卫只是一个体验优化不是安全保障。这个逻辑之前项目里经常被忽略虽然后端有权限校验但没有前端守卫用户点一下管理员链接直接跳到403页面体验很差。4.3 帖子列表、发布页和评论区的核心组件写法帖子列表页的核心是一个带分页的列表组件。我用的Element Plus的el-table在管理后台展示数据前端用普通卡片列表展示帖子。这里关键点是分页组件的封装页码、每页条数、总数这三个数据要跟后端的返回值对应上后端返回的数据结构我统一为{ code: 200, data: { records: [], total: 100 } }因此前端封装分页组件时只需要拿currentPage和pageSize去查接口拿到total后渲染分页。发帖页面稍微有点复杂因为涉及富文本编辑框。Element Plus自带富文本组件不太够用我集成了wangEditor这是一个中文生态很好的富文本编辑器支持图片上传。图片上传后返回URL然后嵌入到内容里。这里有一个体验细节用户填写内容时可能会粘贴网络图片这些图片的URL如果指向外部域名最终展示时可能因为防盗链裂开。我的解决方案是在后端增加一个图片下载代理接口前端保存内容之前把内容里的外链图片URL全部通过代理接口拉取一遍并转存到本地服务器。当然这只是其中一种方案也可以直接用阿里云OSS或腾讯云COS论坛生产环境一般不会把图片存在服务器本地因为磁盘很快就爆了。评论区组件要用递归组件才能优雅地实现楼层式回复。顶层评论和回复不能混在一张列表里渲染而是先按时间排好顶层评论每条顶层评论下面挂它的回复。我这个版本先做两层评论和回复回复里不再嵌套回复回复一个楼层直接显示“回复xxx”的文字提示。如果做无限嵌套对数据库和前端递归组件的性能都有挑战多数论坛也只是做到两层嵌套嵌套再深用户阅读体验也不一定好。script setup const props defineProps({ comments: { type: Array, default: () [] } }) const emit defineEmits([reply]) /script template div classcomment-list div v-forcomment in comments :keycomment.id classcomment-item div classcomment-header el-avatar :srccomment.avatar / span classusername{{ comment.username }}/span span classtime{{ comment.createTime }}/span /div div classcomment-content{{ comment.content }}/div el-button link typeprimary clickemit(reply, comment) 回复 /el-button /div /div /template5. 环境搭建与前后端联调实战5.1 开发环境准备清单后端需要JDK 17以上我用的是SpringBoot 3.2最低要求Java 17如果电脑上还是Java 8建项目时要选SpringBoot 2.7系列的版本Maven 3.8以上IDE我用的是IntelliJ IDEA。前端需要Node.js 18以上npm 9以上IDE用VS Code。数据库就是MySQL 8.0另外建议装一个Navicat或者DataGrip来管理数据库不推荐命令行直接建表字段多的话效率太低。初次搭环境的时候最容易卡在JDK版本上。现在很多教程还是基于JDK 8写的如果你下载的SpringBoot项目是3.x版本直接用JDK 8启动会报Unsupported class file major version。我的建议是安装JDK 17然后在IDEA里Project Structure中修改Project SDK为17并且Maven配置里也指定Java 17。一旦编译环境、运行环境、Maven环境三处的Java版本不一致就会出现“代码能编译但启动时报错”的诡异问题。5.2 后端项目启动步骤与配置文件后端启动流程用IDEA打开项目后等待Maven把依赖下载完第一次会比较慢可以考虑把Maven镜像改成阿里的然后修改application.yml里的数据库连接信息确保用户名密码正确然后创建一个空的数据库forum不要手工建表通过项目里的schema.sql或者MyBatis逆向生成工具来完成建表。启动ForumApplication.java主类看到Started ForumApplication in xx seconds就说明成功了。application.yml里需要关注这几项配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/forum?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.forum.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImplserverTimezoneAsia/Shanghai这个参数一定要加。如果不加虽然有时候默认也能跑通但插入时间字段时会偏差8小时排查起来很隐蔽。log-impl配置成StdOutImpl后控制台会打印所有执行的SQL语句调试的时候特别有用。生产环境建议把这个去掉日志输出量太大了。5.3 前后端联调中的关键设置前端开发服务器默认跑在5173端口后端接口在8080端口直接请求必然会遇到跨域问题。解决跨域有两个方向前端开代理或者后端开CORS。我推荐前端开代理因为这种方式在生产环境也更灵活。在vite.config.js里配置export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这样前端代码里的/api/post/list请求在开发阶段会自动转发到http://localhost:8080/api/post/list。生产环境则根据实际情况用Nginx把/api反向代理到后端。如果你直接在后端写了CORS配置也能解决但要小心不能写成allowedOrigin(*)因为涉及到携带cookie和Authorization头通配符在一些浏览器策略下会导致请求失败。联调阶段我还会让后端的统一返回结构保持严格一致成功时code200业务异常时code400或500未登录时code401无权限时code403。前端Axios拦截器只根据这些code做全局处理就能保证错误提示风格统一。如果每个接口的返回结构都不一样前端响应拦截器就没法写通用逻辑。6. 常见问题与排查技巧实录6.1 跨域请求失败的各种表现与定位方法跨域问题是前后端分离项目里最常见的报错。如果浏览器控制台出现Access to XMLHttpRequest at http://localhost:8080/... from origin http://localhost:5173 has been blocked by CORS policy说明跨域没配置好。如果你配了Vite代理仍然报404那要检查前端请求路径是不是带了/api前缀而后端接口实际路径也是/api开头的通过代理转发时changeOrigin会重写请求头的Host但不会自动去掉/api前缀。排查跨域问题有个小技巧打开浏览器开发者工具看Network面板里请求的真实URL。如果是http://localhost:8080/api/...那说明代理没生效如果是http://localhost:5173/api/...但报错403或404说明代理生效了但后端路由没匹配上。先分清是前端代理的问题还是后端接口的问题再针对性排查比在代码里乱猜好得多。另外如果后端开了CORS又配了前端代理两个同时工作会出现重复的CORS头某些情况下浏览器会拒绝。所以我的做法是开发环境用前端代理后端不配CORS避免混淆。6.2 MyBatis查询结果为空但SQL能跑通的情况有时候页面接口返回空数据但把SQL日志里打印出来的语句放到Navicat里执行明明能查到记录。这种情况多数是实体类字段映射出了问题。比如数据库字段叫create_time实体类属性是createTime如果没有开启map-underscore-to-camel-case就会映射不上查询结果里该字段就是null。但也有一种恰好相反的情况数据库字段是createTime没下划线实体类属性也是createTime按理说没问题但你把XML里的resultType写成了某个DTODTO里恰好没有这个字段那也会读不到。这类问题没什么快捷方式只能逐个字段对比。我后来写了一个简单的检查习惯每次新开发一个接口先用Postman调用一次看返回的JSON和数据库里实际字段是否一致不一致就直接看映射配置。等量积累得多了扫一眼代码就能猜到是哪一类问题。6.3 事务注解不生效的几个常见原因Transactional不生效这个问题论坛项目的评论计数场景里最容易触发。比如删除一个帖子时要先逻辑删除post记录再把帖子对应板块的帖子数减一同时把用户发帖数减一。三个操作在一个方法里如果不加事务中途任何一个失败都会导致数据对不上。加了Transactional仍然不生效通常有以下几个原因第一方法不是public。Spring的声明式事务基于AOP代理而JDK动态代理只能拦截public方法。如果你写了一个私有的辅助方法并加了Transactional它不会生效。第二同类内部调用。同一个类里methodA调用同一个类里的methodBmethodB的事务注解会被忽略因为代理是通过类做的内部调用直接走的是一个普通方法没有经过代理对象。第三数据库引擎不支持事务。MyISAM引擎是不支持事务的需要把表引擎改成InnoDB。检查这三个点绝大部分事务失效问题都能定位。6.4 前端页面常见体验问题登录状态丢失与页面刷新404用户刷新页面后莫名被踢回登录页这是很多论坛项目的通病。原因是登录状态只存在Pinia的store里刷新时store重置token不存在了然后路由守卫发现没有token就跳回登录页。解决方式是在Pinia初始化时从localStorage里读取token。但如果只存token不存用户信息刷新后又要请求一次/user/info接口来还原用户状态这一步我通常放在路由守卫的beforeEach里遇到页面刷新但token还在的情况就异步拉取用户信息塞回store中。前端页面刷新404的问题也很常见尤其是用history路由模式时直接访问/post/123刷新会出现404。开发环境下Vite是能处理的但生产环境部署到Nginx后需要配置try_files $uri $uri/ /index.html;把所有请求都重写回index.html由前端路由接管。如果忘了这个配置用户分享一个帖子链接别人点开直接是404页面相当影响体验。7. 这个项目后续还能怎么扩展论坛系统做一个能用的版本不难但要做到“好用”还有很多可以沉淀的地方。我在做完这个版本之后的体会是技术框架只是骨架真正花时间的往往是那些看起来不起眼的小功能用户头像上传后如果不做裁剪压缩服务器上会积满几兆的大图帖子内容如果不做敏感词过滤平台随时可能面临内容合规风险接口如果没有限流一个简单脚本就能把注册接口打到瘫痪。如果你手头要拿这个项目去面试或者继续扩展我建议优先补这几块接入Spring Cache加Redis来做热帖缓存访问排行榜、热门板块这些查询很吃缓存把图片存储替换为OSS对象存储减少服务器磁盘压力集成Elasticsearch前先用MySQL的全文索引撑一段时间用Docker Compose把MySQL、Redis、后端服务打包这样部署到服务器上时一条命令全部拉起。这些方向都是论坛系统从“玩具”走向“可上线状态”的必经之路补了之后项目的技术广度和深度都会有提升。后端还有一个容易被忽视但值得优化的点接口参数校验。现在很多接口只是判断了参数是否为空但像帖子标题最大长度、评论内容单词长度这类约束分散在各处代码里。建议引入Spring的Validated注解结合实体类的NotBlank、Size校验把参数规则集中在DTO里管理代码会干净很多也避免少校验一个字段导致脏数据入库。前端虽然也做了表单校验但后端必须独立再做一遍前端校验只是用户体验后端校验才是数据安全的底线。如果你计划把这个项目作为毕业设计建议用数据库初始化脚本把一些示例板块数据放进去默认有一个管理员账号、一个测试用户账号演示的时候不需要一个个去创建用户再发帖再回复效果会好很多。我就是在初始化脚本里预置了“Java技术讨论区”“职场交流区”“资源分享区”三个板块还放了几条热门测试帖子这样前端页面一启动就有数据可看评委不会觉得系统是空壳。这套思路在很多同类项目管理系统中都通用预置演示数据是展示项目的最佳方式之一。-- 初始化示例数据 INSERT INTO user (username, password, nickname, role, status, create_time, update_time) VALUES (admin, $2a$10$xxxxxxxxxxxx, Super Admin, 1, 0, NOW(), NOW()); INSERT INTO user (username, password, nickname, role, status, create_time, update_time) VALUES (test_user, $2a$10$yyyyyyyyyyyy, 测试用户, 0, 0, NOW(), NOW()); INSERT INTO category (name, description, sort, create_time, update_time) VALUES (Java技术讨论区, SpringBoot、MyBatis、微服务相关技术交流, 1, NOW(), NOW()); INSERT INTO category (name, description, sort, create_time, update_time) VALUES (职场与生活, 程序员的职场、生活、学习心得, 2, NOW(), NOW());密码这块注意绝不能明文存储用户密码。我在项目中使用的是BCryptPasswordEncoder数据库里存的是加盐哈希后的字符串即使数据被拖库用户原始密码也不会直接泄露。注册接口里需要实现的是接收明文密码经过BCryptPasswordEncoder.encode()后再入库登录时用matches()方法校验。很多初学项目都用MD5加盐但MD5的碰撞在现代硬件下已经很容易制造安全强度不够直接上BCrypt是成本最低且专业的选择。补充一个容易被忽略的细节user表里的status字段0表示正常1表示封禁。登录校验时除了比对用户名密码还要检查这个字段封禁用户即使密码正确也不能登录。这个逻辑我在初版里漏掉了测试时想模拟封禁效果发现封禁之后用户还是会跳转到首页排查了半天才发现登录接口里没读status。类似这种“状态字段只在管理后台改但业务接口不消费”的问题在论坛这类管理系统中很常见做完功能后建议自己把每个状态值都测一遍。说回到整个项目这个SpringBoot加Vue的论坛系统从架构上讲是保守的、经典的一套组合但正因为保守它才容易上手、容易维护、不容易出错。用MyBatis意味着你要对SQL有掌控感用Vue意味着前端可以做得足够灵活两者之间的桥梁是清晰的RESTful接口和统一的数据格式。把基础打牢后续无论往微服务方向演进还是引入更重的前端工程化体系都能站在一个可靠的地基上前进。
返回列表