本文还有配套的精品资源点击获取简介一套开箱即用的社区类应用源码前端用Vue 2.x实现页面交互与路由管理后端基于Spring Boot 2.x搭建RESTful接口数据库采用MySQL附带完整doubao.sql初始化脚本。项目结构规范包含标准Maven配置pom.xml、跨平台启动脚本mvnw/mvnw.cmd、JWT登录鉴权、用户注册登录、发帖、评论等核心社区功能。前后端代码物理分离前端位于独立vue目录后端Java代码集中在src/main/java下配套README.md说明本地运行步骤和基础配置方式。支持跨域访问适合用于学习前后端协作开发、接口调试、Spring Security权限控制、Vue组件通信及MySQL表结构设计无需修改即可在本地完成前后端联调与部署验证。1. 项目概述这不是一个“玩具项目”而是一套能跑通社区闭环的生产级教学样本你有没有遇到过这样的情况网上搜了一堆“VueSpringBoot社区项目”下载下来解压双击IDEA结果报错一堆——前端说找不到axios后端说数据库连接失败README里写的“npm run dev”根本起不来更别说登录发帖了。我试过不下二十个所谓“开源社区模板”八成卡在第一步环境配不齐依赖装不对路径写错了或者干脆就是作者自己都没本地跑通就打包上传了。直到我拿到这个“豆宝社区”压缩包解压、导入、建库、启动前后不到十五分钟首页就刷出来了注册账号、登录、发第一条帖子、评论、刷新页面数据还在——那一刻我才意识到这真不是又一个半成品Demo而是一套经过真实联调验证、结构清晰、边界明确、连新手都能照着走通的全栈教学样本。它叫“豆宝社区”名字听着有点萌但代码一点都不含糊。关键词很直白豆宝社区、Vue2、SpringBoot2、MySQL、JWT鉴权——这五个词就是它的技术DNA也是你打开它的全部钥匙。它不追求最新潮的Vue3 Composition API也不上Spring Boot 3的JDK17而是稳稳踩在Vue 2.6.x Spring Boot 2.7.x JDK8/11这个被企业大量沿用、文档最全、坑最少的黄金组合上。前端是独立的vue/目录不是嵌在后端src/main/resources/static里的静态资源后端是标准Maven结构src/main/java下包路径规整com.doubao.*前缀一目了然数据库脚本doubao.sql不是空壳而是包含用户表、帖子表、评论表、点赞关系表的完整ER模型字段命名规范比如user_id、post_content、created_time有索引、有外键约束、有默认值甚至预置了几条测试数据。它解决的核心问题从来不是“炫技”而是“让学习者第一次真正理解接口怎么定义、前端怎么调、token怎么传、跨域怎么破、数据库怎么初始化、错误怎么定位”。适合谁刚学完Vue基础想练手的前端同学刚撸完Spring Boot入门教程却不知道怎么接前端的后端新人正在带实习生的团队Leader需要一套无争议、无歧义、能直接当教学靶子的项目还有那些准备跳槽、想快速复现一个“可展示、可讲解、可调试”的个人作品集的开发者。它不承诺“一键部署上线”但它保证“本地启动零障碍”。2. 整体架构设计与选型逻辑为什么是Vue2SB2MySQLJWT这不是妥协而是精准卡位很多人看到标题里的“Vue2”和“SpringBoot2”第一反应是“过时了”。但如果你真去翻过一线中小企业的技术栈年报就会发现Vue2在存量管理后台、内部工具系统中占比仍超60%Spring Boot 2.x在金融、政务、制造业等对稳定性要求极高的领域仍是主力版本。这套选型恰恰不是技术保守而是教学场景下的精准卡位——它避开了Vue3的响应式原理重构、Composition API的学习曲线也绕开了Spring Boot 3的Jakarta EE迁移、Spring Security 6的权限模型变更这些容易让初学者迷失的“新概念沼泽”。我们来拆解它的四层架构选择逻辑首先是前端框架选Vue2而非Vue3。Vue2的Options APIdata、methods、computed、mounted结构清晰生命周期钩子命名直白created、mounted、destroyed配合Vue Router 3的beforeEach全局守卫和Vuex 3的状态管理整个前端流程像一条笔直的公路用户点击登录按钮 → 触发login()方法 → 调用this.$http.post(/api/auth/login, data)→ 成功后把返回的token存入localStorage → 跳转到首页 → 在路由守卫里检查token有效性 → 失效则重定向到登录页。这种线性思维对刚脱离jQuery时代的开发者极其友好。而Vue3的setup()函数、ref/reactive、onMounted等API虽然更灵活但初学者很容易陷入“为什么我要先ref再.value”、“为什么computed要写成函数”这类底层困惑反而模糊了“业务逻辑怎么组织”这个核心目标。其次是后端框架锁定Spring Boot 2.7.x。这个版本是Spring Boot 2系列的最终稳定版对JDK8完全兼容同时支持JDK11Maven插件生态成熟spring-boot-maven-plugin 2.7.18内嵌Tomcat 9.0.x性能稳定。更重要的是它搭配的是Spring Security 5.7.x——这是JWT鉴权方案最成熟的版本。Security 5.7将HttpSecurity配置从XML时代彻底转向Java ConfigauthorizeRequests().antMatchers(/api/auth/**).permitAll()这种链式写法比Security 6的authorizeHttpRequests()新语法更直观也更容易对应到“哪些接口公开、哪些需要认证”这种朴素的安全需求。而且它的自动配置机制如EnableWebSecurity和starter依赖spring-boot-starter-security、spring-boot-starter-data-jpa开箱即用不需要你手动配置FilterChainProxy或DataSourceTransactionManager。第三是数据库选用MySQL而非H2或PostgreSQL。doubao.sql脚本里明确写了CREATE DATABASE IF NOT EXISTS doubao DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;并为每张表指定了ENGINEInnoDB。为什么强调这个因为InnoDB支持事务、行级锁、外键约束——这正是社区应用的核心需求用户发帖时要保证“插入帖子记录”和“更新用户发帖数统计”这两个操作要么都成功要么都失败评论点赞时要避免并发导致的重复计数。而H2内存数据库虽然启动快但无法模拟真实MySQL的字符集问题比如emoji存储失败、索引失效场景如LIKE %关键词%无法走索引、主从同步延迟等典型线上问题。用MySQL就是让你从第一天就面对真实世界的约束。最后是鉴权方案采用JWT而非Session。项目里LoginController.java返回的是{code:200,msg:登录成功,data:{token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...}}前端把这个token存在localStorage后续每个请求的Header里都带上Authorization: Bearer token。为什么选JWT因为它完美匹配前后端分离架构后端不用维护session状态所有用户信息都编码在token payload里如{ userId: 123, username: zhangsan, exp: 1735689600 }前端可以随时解析查看用jwt-decode库调试时一眼就能看出token是否过期、用户ID是否正确。而传统Session需要后端存储Redis或内存还要处理sessionId的传递、过期清理、集群共享等问题对初学者来说光是搞懂Cookie SameSite策略就能卡半天。JWT在这里不是为了“高大上”而是为了“看得见、摸得着、改得动”。提示别急着升级版本。我见过太多人把Vue2项目强行升级到Vue3结果Router守卫逻辑全乱Vuex状态丢失最后发现是router.push()的参数格式变了。学习阶段稳定压倒一切。等你把这套流程跑熟了再去看Vue3的Migration Build那才是水到渠成。3. 核心模块解析与实操要点从数据库建模到JWT拦截器每一行代码都有它的理由现在我们钻进代码细节。别被目录树里那些.gitignore.hoist-conflict-*或新建文本文档 (2).txt干扰它们是下载过程中的临时文件直接删掉。真正的骨架就三块doubao.sql数据库脚本、vue/前端目录、src/main/java/com/doubao/后端源码。我们按数据流顺序从底向上拆解。3.1 数据库建模doubao.sql不只是建表它是业务规则的具象化打开doubao.sql你会发现它不是简单地CREATE TABLE user (...)。它是一个完整的初始化流程-- 第一步创建数据库并切换 CREATE DATABASE IF NOT EXISTS doubao DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE doubao; -- 第二步建用户表注意关键字段 CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL UNIQUE COMMENT 用户名唯一, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, email varchar(100) DEFAULT NULL COMMENT 邮箱, avatar_url varchar(255) DEFAULT NULL COMMENT 头像地址, post_count int DEFAULT 0 COMMENT 发帖数冗余字段提升查询效率, created_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_username (username) USING BTREE -- 为登录查询加速 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT用户表; -- 第三步建帖子表外键关联用户 CREATE TABLE post ( id bigint NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 标题, content text NOT NULL COMMENT 正文TEXT类型支持长文本, user_id bigint NOT NULL COMMENT 作者ID外键, like_count int DEFAULT 0 COMMENT 点赞数, comment_count int DEFAULT 0 COMMENT 评论数, status tinyint DEFAULT 1 COMMENT 状态1-正常0-已删除软删除, created_time datetime DEFAULT CURRENT_TIMESTAMP, updated_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) USING BTREE, CONSTRAINT fk_post_user_id FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE -- 级联删除用户删了他的帖子也自动删 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT帖子表;看到这里你应该明白几个设计意图第一post_count和like_count是冗余字段不是每次查帖子都要JOIN user再COUNT()而是用户发帖/点赞时后端代码里直接UPDATE user SET post_count post_count 1 WHERE id ?用空间换时间第二status字段实现软删除而不是DELETE FROM post这样历史数据可追溯管理员也能恢复误删内容第三FOREIGN KEY加了ON DELETE CASCADE这是数据库层面的强一致性保障比后端代码里手动删帖子更可靠。执行这个脚本前记得在MySQL客户端里先SET FOREIGN_KEY_CHECKS 0;如果之前有残留表再SOURCE /path/to/doubao.sql否则外键约束可能报错。3.2 后端核心Spring Security JWT拦截器如何让“未登录不能发帖”变成一行配置后端安全控制的核心在com.doubao.config.SecurityConfig.java。它不是一个大杂烩类而是职责单一的配置类Configuration EnableWebSecurity public class SecurityConfig { Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // 密码必须BCrypt加密和SQL脚本里insert的$2a$10$...格式匹配 } Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() // 前后端分离禁用CSRF它依赖Cookie而JWT用Header .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 完全无状态不创建HttpSession .and() .authorizeRequests() .antMatchers(/api/auth/**).permitAll() // /api/auth/login 和 /api/auth/register 公开 .antMatchers(HttpMethod.GET, /api/posts/**).permitAll() // 所有GET帖子接口公开游客可看 .antMatchers(/api/**).authenticated() // 其他所有/api/下的接口必须登录 .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 在登录过滤器前插入JWT校验 return http.build(); } Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); // 这个自定义Filter是关键 } }重点看JwtAuthenticationFilter。它继承OncePerRequestFilter确保每个请求只执行一次。核心逻辑在doFilterInternal方法Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader request.getHeader(Authorization); // 从Header取Authorization if (authHeader ! null authHeader.startsWith(Bearer )) { String token authHeader.substring(7); // 截取Bearer后面的token字符串 try { Long userId jwtUtil.getUserIdFromToken(token); // 解析token拿到userId User userDetails userService.findById(userId); // 根据userId查用户详情 UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); // 把用户信息塞进Spring Security上下文 } catch (Exception e) { // token无效、过期、签名错误一律不设置Authentication后续authenticated()检查会失败 } } filterChain.doFilter(request, response); // 放行让请求继续往后走 }这段代码解释了“为什么登录后能发帖”当你在前端调用axios.post(/api/posts, {title:hello})时请求头自动带上Authorization: Bearer xxx这个Filter截获请求解析出你的userId123查出User{id123, usernamezhangsan, authorities[ROLE_USER]}然后放进SecurityContextHolder等到PostController.createPost()方法执行时Spring Security的PreAuthorize(hasRole(USER))注解就会从上下文中取出这个用户并检查角色——匹配成功放行如果不带token或token错误SecurityContextHolder里是空的authenticated()检查失败直接返回401 Unauthorized。整个过程没有session没有cookie全是Header和Filter的协作。3.3 前端交互Vue组件如何与后端API“说同一种语言”前端代码在vue/目录下结构是标准Vue CLI 3.x生成的vue/ ├── public/ │ └── index.html ├── src/ │ ├── api/ # 所有API请求封装 │ │ ├── auth.js # 登录/注册 │ │ ├── post.js # 帖子相关 │ │ └── comment.js # 评论相关 │ ├── components/ # 可复用组件Header、Footer、PostItem │ ├── router/ # Vue Router配置 │ │ └── index.js # 路由守卫在这里 │ ├── store/ # Vuex状态管理 │ │ └── index.js # 存token、用户信息 │ ├── App.vue │ └── main.js # 入口配置axios全局拦截器最关键的是main.js里对axios的配置import axios from axios // 设置基础URL axios.defaults.baseURL http://localhost:8080/api // 后端Spring Boot默认端口 // 请求拦截器每次发请求前自动把token塞进Header axios.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} // 和后端Filter解析逻辑严格对应 } return config }) // 响应拦截器统一处理401错误 axios.interceptors.response.use( response response, error { if (error.response?.status 401) { // 清空本地token跳转到登录页 localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )再看src/api/post.js里一个典型的发帖方法export function createPost(data) { return axios.post(/posts, data) // 注意这里是/posts因为baseURL已经设为http://localhost:8080/api }而在components/CreatePost.vue里调用它template form submit.preventhandleSubmit input v-modelform.title placeholder标题 / textarea v-modelform.content placeholder内容 / button typesubmit发布/button /form /template script import { createPost } from /api/post export default { data() { return { form: { title: , content: } } }, methods: { async handleSubmit() { try { const res await createPost(this.form) // 调用API this.$message.success(发布成功) this.$router.push(/post/${res.data.id}) // 跳转到新帖子详情页 } catch (error) { this.$message.error(发布失败 error.response?.data?.msg || 网络错误) } } } } /script这里体现了前后端协作的精髓前端只关心“我要发什么数据title/content”后端只关心“我收到了什么数据该怎么存”。中间的协议HTTP Method、URL Path、Header Key、Status Code是双方约定好的契约。createPost(this.form)这一行背后是axios拼接URL、添加Header、发送JSON、等待响应、抛出错误的完整链路。而error.response?.data?.msg能取到后端返回的{code:500,msg:标题不能为空,data:null}说明后端Controller里做了参数校验NotBlank注解并统一包装了错误响应体。这种“契约驱动”的开发模式才是工程化的起点。4. 实操全流程从零开始15分钟完成本地启动与功能验证现在我们把前面所有的理论变成键盘上的真实操作。整个过程我建议你严格按顺序执行不要跳步尤其不要在没建库前就启动后端——那只会看到满屏红色ERROR。4.1 环境准备确认三件套已就位你需要提前装好-JDK 8 或 JDK 11在命令行输入java -version输出类似openjdk version 11.0.20即可。Spring Boot 2.7.x不支持JDK17。-Node.js 14.x 或 16.x输入node -v输出v14.21.3或v16.20.2。Vue2 CLI 3.x对Node版本有要求太高如18.x会报错。-MySQL 5.7 或 8.0输入mysql --version确认已安装。推荐用Docker快速启动docker run -d --name mysql-doubao -p 3306:3306 -e MYSQL_ROOT_PASSWORDroot -e MYSQL_DATABASEdoubao -d mysql:8.0。注意Windows用户请确保mvnw.cmd和mvnw两个启动脚本都在根目录。Mac/Linux用户只需用./mvnwWindows用mvnw.cmd。这是Maven Wrapper它会自动下载对应版本的Maven无需你单独安装Maven。4.2 数据库初始化执行doubao.sql的正确姿势打开MySQL客户端命令行或Navicat/MySQL Workbench。如果用命令行先登录mysql -u root -p输入密码root如果你用的是Docker命令密码就是root。创建数据库如果还没建CREATE DATABASE doubao DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;切换数据库USE doubao;执行SQL脚本SOURCE /path/to/your/download/doubao.sql;把/path/to/...替换成你实际的路径。如果报错ERROR 1064大概率是脚本开头有BOM头或编码问题用VS Code打开doubao.sql右下角点“UTF-8”选“Save with Encoding” → “UTF-8”再保存一次。验证执行SELECT COUNT(*) FROM user;应该返回0因为脚本里只建表没插测试用户需要你注册执行SHOW TABLES;应该看到user,post,comment,like_record等表名。4.3 后端启动修改配置运行mvnw打开IDEA或VS Code打开项目根目录就是有pom.xml的那个文件夹。找到src/main/resources/application.yml检查数据库配置yaml spring: datasource: url: jdbc:mysql://localhost:3306/doubao?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root # 改成你MySQL的实际密码如果你用的是Dockerlocalhost要改成host.docker.internalMac/Windows或宿主机IPLinux。在IDEA里右键com.doubao.DoubaoApplication.java→Run DoubaoApplication或者在终端进入项目根目录执行bash ./mvnw spring-boot:run # Mac/Linux mvnw.cmd spring-boot:run # Windows等待控制台输出Started DoubaoApplication in X.XXX seconds并且最后一行是Tomcat started on port(s): 8080 (http)说明后端启动成功。此时访问http://localhost:8080/api/auth/test一个内置的测试接口应该返回{code:200,msg:test ok,data:null}。4.4 前端启动进入vue/目录npm install再npm run serve打开一个新的终端窗口不要关掉后端那个。进入vue/目录cd vue安装依赖npm install如果卡在node-sass可以先npm install node-sass4.14.1再npm installVue2 CLI 3.x兼容这个版本。启动前端npm run serve等待输出App running at: - Local: http://localhost:8081/说明前端已就绪。打开浏览器访问http://localhost:8081首页应该正常显示。点击右上角“注册”填入用户名、密码、邮箱提交。此时后端控制台会打印INSERT INTO user ...的日志数据库user表里多了一条记录。注册完用刚注册的账号登录。登录成功后前端会把token存入localStorage你可以按F12打开开发者工具 → Application → Local Storage →http://localhost:8081看到token字段。点击“发帖”填写标题和内容点击发布。打开Network面板找到POST /api/posts请求Headers里能看到Authorization: Bearer eyJhbGciOi...Preview里能看到返回的帖子ID。刷新页面帖子列表里就出现了你的新帖。整个流程走通意味着你已经完成了从数据库建模、后端API开发、前端页面渲染、JWT鉴权、跨域通信的全链路验证。这不是“Hello World”而是真实的社区功能闭环。5. 常见问题与排查技巧实录那些让我重启三次IDEA的坑现在都给你列明白了在帮十几个学员部署这个项目的过程中我整理了一份高频问题清单。这些问题90%都源于环境差异或操作细节疏忽而不是代码本身有Bug。我把它们按发生阶段分类并附上我的“秒级定位法”。5.1 后端启动失败Failed to configure a DataSource现象IDEA控制台一启动就报红核心错误是Consider defining a bean of type javax.sql.DataSource in your configuration.后面跟着一堆Caused by: java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因与解法-Driver类找不到pom.xml里mysql-connector-java依赖版本太低如5.1.47而MySQL 8.0要用8.0.33。打开pom.xml找到artifactIdmysql-connector-java/artifactId把version改成8.0.33然后右键项目 →Maven→Reload project。-数据库URL格式错误MySQL 8.0强制要求serverTimezone参数。检查application.yml里的url必须包含?serverTimezoneAsia/Shanghai且useSSLfalse如果没配SSL证书。-密码错误或数据库不存在application.yml里password写错了或者doubao数据库根本没创建。用MySQL客户端手动连一下mysql -u root -p -h localhost -P 3306 doubao能连上说明配置没问题。5.2 前端无法访问后端APICORS error现象前端页面空白F12看Console报Access to XMLHttpRequest at http://localhost:8080/api/auth/login from origin http://localhost:8081 has been blocked by CORS policy.原因与解法-后端跨域配置被覆盖SecurityConfig.java里http.cors()没开。在filterChain()方法里http.csrf().disable()下面加上.cors().configurationSource(corsConfigurationSource())并新增一个Bean方法java Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList(http://localhost:8081)); // 前端地址 configuration.setAllowedMethods(Arrays.asList(GET, POST, PUT, DELETE, OPTIONS)); configuration.setAllowCredentials(true); configuration.addAllowedOrigin(http://localhost:8081); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, configuration); return source; }-前端axios baseURL写错检查vue/src/main.jsaxios.defaults.baseURL必须是http://localhost:8080/api不能少http://也不能写成/api那是相对路径会变成http://localhost:8081/api请求发给了前端自己。5.3 登录成功但无法发帖403 Forbidden现象登录接口返回200token也存进了localStorage但点“发帖”按钮Network里POST /api/posts返回403。原因与解法-JWT Filter没生效检查SecurityConfig.java里addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)这行UsernamePasswordAuthenticationFilter.class必须拼写正确注意大小写且jwtAuthenticationFilter()方法返回的Bean必须是JwtAuthenticationFilter实例不能是new JwtAuthenticationFilter()那样就不是Spring管理的Bean了。-token过期或格式错误登录后立刻打开F12 → Application → Local Storage复制token值粘贴到https://jwt.io解码。看payload里exp过期时间是不是过去的时间戳看userId是不是数字而不是字符串123后端jwtUtil.getUserIdFromToken()方法里用了Long.parseLong()如果是字符串会抛异常Filter就静默失败了。5.4 页面样式错乱或组件不渲染现象首页加载出来但导航栏没了或者帖子列表是空白的Console里报[Vue warn]: Property or method posts is not defined on the instance。原因与解法-Vue CLI版本冲突vue/package.json里vue-cli-service版本如果是^4.5.0而你全局装了Vue CLI 5.x会导致构建失败。解决方案删除vue/node_modules和vue/package-lock.json然后npm install重新装确保装的是vue-cli-service4.5.15。-ESLint校验阻断vue/src/main.js里如果有console.log()ESLint会报错阻止编译。在vue/vue.config.js里加js module.exports { lintOnSave: false // 关闭保存时校验 }我把这些坑总结成一张速查表方便你随时对照问题现象最可能原因快速验证法修复命令/操作后端启动报DataSource错MySQL驱动版本不匹配mvn dependency:tree \| grep mysql改pom.xml里mysql-connector-java版本为8.0.33前端报CORS错误后端没开CORS或URL写错浏览器Network里看请求发到了哪个域名检查application.yml的url和main.js的baseURL登录后403发不了帖JWT Filter未注入Spring容器IDEA里CtrlClickjwtAuthenticationFilter()看能否跳转到定义确保Bean方法返回类型是JwtAuthenticationFilter页面空白Console报Vue warnvue-cli-service版本过高cd vue npm list vue-cli-service删除node_modulesnpm install重装实操心得每次遇到问题先做三件事1. 看后端控制台最后一屏日志不是最上面是启动完成后你操作时的日志2. 看前端Network面板里具体哪个请求失败、状态码多少、Response里是什么内容3. 打开F12的Console看有没有JS报错。90%的问题答案就在这三个地方。别一上来就怀疑代码有Bug先怀疑自己的环境和操作。6. 二次开发与能力延伸从“能跑”到“能改”这才是项目真正的价值现在你已经能让豆宝社区在本地跑起来了。但这只是起点。这个项目的真正价值在于它为你提供了一个“可触摸、可修改、可验证”的沙盒。接下来我分享几个我带学员做的、真正提升工程能力的实战练习每一个都紧扣关键词且都有明确的产出目标。6.1 给帖子加富文本编辑器Vue2 wangEditor实战原项目里发帖用的是纯textarea用户体验差。我们可以集成wangEditorVue2兼容版。步骤很简单1. 在vue/目录下npm install wangeditor4.7.12注意必须是4.x5.x只支持Vue3。2. 在components/CreatePost.vue里vue发布 3. 后端PostController.createPost()方法里RequestBody PostDTO dto的content字段接收的就是HTML字符串直接存入MySQL的content字段TEXT类型完全支持。 **收获**你学会了如何在Vue2项目里集成第三方UI组件理解了mounted/beforeDestroy生命周期的用途也实践了前后端对富文本的协同处理。6.2 实现帖子搜索功能MySQL全文索引 Spring Data JPA用户想搜“Vue教程”当前只能靠LIKE %Vue%效率低。我们可以用MySQL全文索引1. 在MySQL里执行sql ALTER TABLE post ADD FULLTEXT(title, content); -- 为标题和内容建全文索引2. 在后端PostRepository.java里加一个自定义查询方法java public interface PostRepository extends JpaRepositoryPost, Long { Query(value SELECT * FROM post WHERE MATCH(title, content) AGAINST(?1 IN NATURAL LANGUAGE MODE), nativeQuery true) ListPost searchByKeyword(String keyword); }3. 在PostService.java里暴露服务java public ListPost searchPosts(String keyword) { return postRepository.searchByKeyword(keyword); }4. 前端src/api/post.js里加searchPosts(keyword)方法components/SearchBar.vue里调用。收获你亲手给数据库加了索引写了原生SQL查询理解了MATCH...AGAINST和LIKE的本质区别前者用索引后者全表扫描也掌握了Spring Data JPA的Query用法。6.3 将JWT Token存入HttpOnly Cookie增强安全性目前token存在localStorage有XSS风险。我们可以改成存入HttpOnlyCookie1. 后端LoginController.login()里登录成功后不返回token而是java Cookie cookie new Cookie(AUTH_TOKEN, jwtUtil.generateToken(user)); cookie.setHttpOnly(true); cookie.setPath(/); cookie.setMaxAge(3600); // 1小时 response.addCookie(cookie);2. 前端main.js里axios请求拦截器改为从Cookie读token需引入js-cookie库javascript import Cookies from js-cookie axios.interceptors.request.use(config { const token Cookies.get(AUTH_TOKEN) if (token) { config.headers.Authorization Bearer ${token} } return config })收获你实践了Web安全的核心原则——敏感信息不存前端JS可访问的地方理解了HttpOnly的作用并完成了前后端Cookie传输的完整链路。这三个练习没有一个是“为了炫技”每一个都直指真实开发中的痛点编辑体验、搜索性能、安全加固。当你做完你就不再是一个“能跑Demo的人”而是一个“知道问题在哪、方案怎么选、代码怎么写、效果怎么验”的合格开发者。豆宝社区的价值正在于此——它是一块砖你拿它垫脚够得着更高的地方。本文还有配套的精品资源点击获取简介一套开箱即用的社区类应用源码前端用Vue 2.x实现页面交互与路由管理后端基于Spring Boot 2.x搭建RESTful接口数据库采用MySQL附带完整doubao.sql初始化脚本。项目结构规范包含标准Maven配置pom.xml、跨平台启动脚本mvnw/mvnw.cmd、JWT登录鉴权、用户注册登录、发帖、评论等核心社区功能。前后端代码物理分离前端位于独立vue目录后端Java代码集中在src/main/java下配套README.md说明本地运行步骤和基础配置方式。支持跨域访问适合用于学习前后端协作开发、接口调试、Spring Security权限控制、Vue组件通信及MySQL表结构设计无需修改即可在本地完成前后端联调与部署验证。本文还有配套的精品资源点击获取
豆宝社区开源项目:Vue2+SpringBoot2全栈可运行代码包(含MySQL建库脚本)
本文还有配套的精品资源点击获取简介一套开箱即用的社区类应用源码前端用Vue 2.x实现页面交互与路由管理后端基于Spring Boot 2.x搭建RESTful接口数据库采用MySQL附带完整doubao.sql初始化脚本。项目结构规范包含标准Maven配置pom.xml、跨平台启动脚本mvnw/mvnw.cmd、JWT登录鉴权、用户注册登录、发帖、评论等核心社区功能。前后端代码物理分离前端位于独立vue目录后端Java代码集中在src/main/java下配套README.md说明本地运行步骤和基础配置方式。支持跨域访问适合用于学习前后端协作开发、接口调试、Spring Security权限控制、Vue组件通信及MySQL表结构设计无需修改即可在本地完成前后端联调与部署验证。1. 项目概述这不是一个“玩具项目”而是一套能跑通社区闭环的生产级教学样本你有没有遇到过这样的情况网上搜了一堆“VueSpringBoot社区项目”下载下来解压双击IDEA结果报错一堆——前端说找不到axios后端说数据库连接失败README里写的“npm run dev”根本起不来更别说登录发帖了。我试过不下二十个所谓“开源社区模板”八成卡在第一步环境配不齐依赖装不对路径写错了或者干脆就是作者自己都没本地跑通就打包上传了。直到我拿到这个“豆宝社区”压缩包解压、导入、建库、启动前后不到十五分钟首页就刷出来了注册账号、登录、发第一条帖子、评论、刷新页面数据还在——那一刻我才意识到这真不是又一个半成品Demo而是一套经过真实联调验证、结构清晰、边界明确、连新手都能照着走通的全栈教学样本。它叫“豆宝社区”名字听着有点萌但代码一点都不含糊。关键词很直白豆宝社区、Vue2、SpringBoot2、MySQL、JWT鉴权——这五个词就是它的技术DNA也是你打开它的全部钥匙。它不追求最新潮的Vue3 Composition API也不上Spring Boot 3的JDK17而是稳稳踩在Vue 2.6.x Spring Boot 2.7.x JDK8/11这个被企业大量沿用、文档最全、坑最少的黄金组合上。前端是独立的vue/目录不是嵌在后端src/main/resources/static里的静态资源后端是标准Maven结构src/main/java下包路径规整com.doubao.*前缀一目了然数据库脚本doubao.sql不是空壳而是包含用户表、帖子表、评论表、点赞关系表的完整ER模型字段命名规范比如user_id、post_content、created_time有索引、有外键约束、有默认值甚至预置了几条测试数据。它解决的核心问题从来不是“炫技”而是“让学习者第一次真正理解接口怎么定义、前端怎么调、token怎么传、跨域怎么破、数据库怎么初始化、错误怎么定位”。适合谁刚学完Vue基础想练手的前端同学刚撸完Spring Boot入门教程却不知道怎么接前端的后端新人正在带实习生的团队Leader需要一套无争议、无歧义、能直接当教学靶子的项目还有那些准备跳槽、想快速复现一个“可展示、可讲解、可调试”的个人作品集的开发者。它不承诺“一键部署上线”但它保证“本地启动零障碍”。2. 整体架构设计与选型逻辑为什么是Vue2SB2MySQLJWT这不是妥协而是精准卡位很多人看到标题里的“Vue2”和“SpringBoot2”第一反应是“过时了”。但如果你真去翻过一线中小企业的技术栈年报就会发现Vue2在存量管理后台、内部工具系统中占比仍超60%Spring Boot 2.x在金融、政务、制造业等对稳定性要求极高的领域仍是主力版本。这套选型恰恰不是技术保守而是教学场景下的精准卡位——它避开了Vue3的响应式原理重构、Composition API的学习曲线也绕开了Spring Boot 3的Jakarta EE迁移、Spring Security 6的权限模型变更这些容易让初学者迷失的“新概念沼泽”。我们来拆解它的四层架构选择逻辑首先是前端框架选Vue2而非Vue3。Vue2的Options APIdata、methods、computed、mounted结构清晰生命周期钩子命名直白created、mounted、destroyed配合Vue Router 3的beforeEach全局守卫和Vuex 3的状态管理整个前端流程像一条笔直的公路用户点击登录按钮 → 触发login()方法 → 调用this.$http.post(/api/auth/login, data)→ 成功后把返回的token存入localStorage → 跳转到首页 → 在路由守卫里检查token有效性 → 失效则重定向到登录页。这种线性思维对刚脱离jQuery时代的开发者极其友好。而Vue3的setup()函数、ref/reactive、onMounted等API虽然更灵活但初学者很容易陷入“为什么我要先ref再.value”、“为什么computed要写成函数”这类底层困惑反而模糊了“业务逻辑怎么组织”这个核心目标。其次是后端框架锁定Spring Boot 2.7.x。这个版本是Spring Boot 2系列的最终稳定版对JDK8完全兼容同时支持JDK11Maven插件生态成熟spring-boot-maven-plugin 2.7.18内嵌Tomcat 9.0.x性能稳定。更重要的是它搭配的是Spring Security 5.7.x——这是JWT鉴权方案最成熟的版本。Security 5.7将HttpSecurity配置从XML时代彻底转向Java ConfigauthorizeRequests().antMatchers(/api/auth/**).permitAll()这种链式写法比Security 6的authorizeHttpRequests()新语法更直观也更容易对应到“哪些接口公开、哪些需要认证”这种朴素的安全需求。而且它的自动配置机制如EnableWebSecurity和starter依赖spring-boot-starter-security、spring-boot-starter-data-jpa开箱即用不需要你手动配置FilterChainProxy或DataSourceTransactionManager。第三是数据库选用MySQL而非H2或PostgreSQL。doubao.sql脚本里明确写了CREATE DATABASE IF NOT EXISTS doubao DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;并为每张表指定了ENGINEInnoDB。为什么强调这个因为InnoDB支持事务、行级锁、外键约束——这正是社区应用的核心需求用户发帖时要保证“插入帖子记录”和“更新用户发帖数统计”这两个操作要么都成功要么都失败评论点赞时要避免并发导致的重复计数。而H2内存数据库虽然启动快但无法模拟真实MySQL的字符集问题比如emoji存储失败、索引失效场景如LIKE %关键词%无法走索引、主从同步延迟等典型线上问题。用MySQL就是让你从第一天就面对真实世界的约束。最后是鉴权方案采用JWT而非Session。项目里LoginController.java返回的是{code:200,msg:登录成功,data:{token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...}}前端把这个token存在localStorage后续每个请求的Header里都带上Authorization: Bearer token。为什么选JWT因为它完美匹配前后端分离架构后端不用维护session状态所有用户信息都编码在token payload里如{ userId: 123, username: zhangsan, exp: 1735689600 }前端可以随时解析查看用jwt-decode库调试时一眼就能看出token是否过期、用户ID是否正确。而传统Session需要后端存储Redis或内存还要处理sessionId的传递、过期清理、集群共享等问题对初学者来说光是搞懂Cookie SameSite策略就能卡半天。JWT在这里不是为了“高大上”而是为了“看得见、摸得着、改得动”。提示别急着升级版本。我见过太多人把Vue2项目强行升级到Vue3结果Router守卫逻辑全乱Vuex状态丢失最后发现是router.push()的参数格式变了。学习阶段稳定压倒一切。等你把这套流程跑熟了再去看Vue3的Migration Build那才是水到渠成。3. 核心模块解析与实操要点从数据库建模到JWT拦截器每一行代码都有它的理由现在我们钻进代码细节。别被目录树里那些.gitignore.hoist-conflict-*或新建文本文档 (2).txt干扰它们是下载过程中的临时文件直接删掉。真正的骨架就三块doubao.sql数据库脚本、vue/前端目录、src/main/java/com/doubao/后端源码。我们按数据流顺序从底向上拆解。3.1 数据库建模doubao.sql不只是建表它是业务规则的具象化打开doubao.sql你会发现它不是简单地CREATE TABLE user (...)。它是一个完整的初始化流程-- 第一步创建数据库并切换 CREATE DATABASE IF NOT EXISTS doubao DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE doubao; -- 第二步建用户表注意关键字段 CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL UNIQUE COMMENT 用户名唯一, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, email varchar(100) DEFAULT NULL COMMENT 邮箱, avatar_url varchar(255) DEFAULT NULL COMMENT 头像地址, post_count int DEFAULT 0 COMMENT 发帖数冗余字段提升查询效率, created_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_username (username) USING BTREE -- 为登录查询加速 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT用户表; -- 第三步建帖子表外键关联用户 CREATE TABLE post ( id bigint NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 标题, content text NOT NULL COMMENT 正文TEXT类型支持长文本, user_id bigint NOT NULL COMMENT 作者ID外键, like_count int DEFAULT 0 COMMENT 点赞数, comment_count int DEFAULT 0 COMMENT 评论数, status tinyint DEFAULT 1 COMMENT 状态1-正常0-已删除软删除, created_time datetime DEFAULT CURRENT_TIMESTAMP, updated_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) USING BTREE, CONSTRAINT fk_post_user_id FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE -- 级联删除用户删了他的帖子也自动删 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT帖子表;看到这里你应该明白几个设计意图第一post_count和like_count是冗余字段不是每次查帖子都要JOIN user再COUNT()而是用户发帖/点赞时后端代码里直接UPDATE user SET post_count post_count 1 WHERE id ?用空间换时间第二status字段实现软删除而不是DELETE FROM post这样历史数据可追溯管理员也能恢复误删内容第三FOREIGN KEY加了ON DELETE CASCADE这是数据库层面的强一致性保障比后端代码里手动删帖子更可靠。执行这个脚本前记得在MySQL客户端里先SET FOREIGN_KEY_CHECKS 0;如果之前有残留表再SOURCE /path/to/doubao.sql否则外键约束可能报错。3.2 后端核心Spring Security JWT拦截器如何让“未登录不能发帖”变成一行配置后端安全控制的核心在com.doubao.config.SecurityConfig.java。它不是一个大杂烩类而是职责单一的配置类Configuration EnableWebSecurity public class SecurityConfig { Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // 密码必须BCrypt加密和SQL脚本里insert的$2a$10$...格式匹配 } Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() // 前后端分离禁用CSRF它依赖Cookie而JWT用Header .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 完全无状态不创建HttpSession .and() .authorizeRequests() .antMatchers(/api/auth/**).permitAll() // /api/auth/login 和 /api/auth/register 公开 .antMatchers(HttpMethod.GET, /api/posts/**).permitAll() // 所有GET帖子接口公开游客可看 .antMatchers(/api/**).authenticated() // 其他所有/api/下的接口必须登录 .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 在登录过滤器前插入JWT校验 return http.build(); } Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); // 这个自定义Filter是关键 } }重点看JwtAuthenticationFilter。它继承OncePerRequestFilter确保每个请求只执行一次。核心逻辑在doFilterInternal方法Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader request.getHeader(Authorization); // 从Header取Authorization if (authHeader ! null authHeader.startsWith(Bearer )) { String token authHeader.substring(7); // 截取Bearer后面的token字符串 try { Long userId jwtUtil.getUserIdFromToken(token); // 解析token拿到userId User userDetails userService.findById(userId); // 根据userId查用户详情 UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); // 把用户信息塞进Spring Security上下文 } catch (Exception e) { // token无效、过期、签名错误一律不设置Authentication后续authenticated()检查会失败 } } filterChain.doFilter(request, response); // 放行让请求继续往后走 }这段代码解释了“为什么登录后能发帖”当你在前端调用axios.post(/api/posts, {title:hello})时请求头自动带上Authorization: Bearer xxx这个Filter截获请求解析出你的userId123查出User{id123, usernamezhangsan, authorities[ROLE_USER]}然后放进SecurityContextHolder等到PostController.createPost()方法执行时Spring Security的PreAuthorize(hasRole(USER))注解就会从上下文中取出这个用户并检查角色——匹配成功放行如果不带token或token错误SecurityContextHolder里是空的authenticated()检查失败直接返回401 Unauthorized。整个过程没有session没有cookie全是Header和Filter的协作。3.3 前端交互Vue组件如何与后端API“说同一种语言”前端代码在vue/目录下结构是标准Vue CLI 3.x生成的vue/ ├── public/ │ └── index.html ├── src/ │ ├── api/ # 所有API请求封装 │ │ ├── auth.js # 登录/注册 │ │ ├── post.js # 帖子相关 │ │ └── comment.js # 评论相关 │ ├── components/ # 可复用组件Header、Footer、PostItem │ ├── router/ # Vue Router配置 │ │ └── index.js # 路由守卫在这里 │ ├── store/ # Vuex状态管理 │ │ └── index.js # 存token、用户信息 │ ├── App.vue │ └── main.js # 入口配置axios全局拦截器最关键的是main.js里对axios的配置import axios from axios // 设置基础URL axios.defaults.baseURL http://localhost:8080/api // 后端Spring Boot默认端口 // 请求拦截器每次发请求前自动把token塞进Header axios.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} // 和后端Filter解析逻辑严格对应 } return config }) // 响应拦截器统一处理401错误 axios.interceptors.response.use( response response, error { if (error.response?.status 401) { // 清空本地token跳转到登录页 localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )再看src/api/post.js里一个典型的发帖方法export function createPost(data) { return axios.post(/posts, data) // 注意这里是/posts因为baseURL已经设为http://localhost:8080/api }而在components/CreatePost.vue里调用它template form submit.preventhandleSubmit input v-modelform.title placeholder标题 / textarea v-modelform.content placeholder内容 / button typesubmit发布/button /form /template script import { createPost } from /api/post export default { data() { return { form: { title: , content: } } }, methods: { async handleSubmit() { try { const res await createPost(this.form) // 调用API this.$message.success(发布成功) this.$router.push(/post/${res.data.id}) // 跳转到新帖子详情页 } catch (error) { this.$message.error(发布失败 error.response?.data?.msg || 网络错误) } } } } /script这里体现了前后端协作的精髓前端只关心“我要发什么数据title/content”后端只关心“我收到了什么数据该怎么存”。中间的协议HTTP Method、URL Path、Header Key、Status Code是双方约定好的契约。createPost(this.form)这一行背后是axios拼接URL、添加Header、发送JSON、等待响应、抛出错误的完整链路。而error.response?.data?.msg能取到后端返回的{code:500,msg:标题不能为空,data:null}说明后端Controller里做了参数校验NotBlank注解并统一包装了错误响应体。这种“契约驱动”的开发模式才是工程化的起点。4. 实操全流程从零开始15分钟完成本地启动与功能验证现在我们把前面所有的理论变成键盘上的真实操作。整个过程我建议你严格按顺序执行不要跳步尤其不要在没建库前就启动后端——那只会看到满屏红色ERROR。4.1 环境准备确认三件套已就位你需要提前装好-JDK 8 或 JDK 11在命令行输入java -version输出类似openjdk version 11.0.20即可。Spring Boot 2.7.x不支持JDK17。-Node.js 14.x 或 16.x输入node -v输出v14.21.3或v16.20.2。Vue2 CLI 3.x对Node版本有要求太高如18.x会报错。-MySQL 5.7 或 8.0输入mysql --version确认已安装。推荐用Docker快速启动docker run -d --name mysql-doubao -p 3306:3306 -e MYSQL_ROOT_PASSWORDroot -e MYSQL_DATABASEdoubao -d mysql:8.0。注意Windows用户请确保mvnw.cmd和mvnw两个启动脚本都在根目录。Mac/Linux用户只需用./mvnwWindows用mvnw.cmd。这是Maven Wrapper它会自动下载对应版本的Maven无需你单独安装Maven。4.2 数据库初始化执行doubao.sql的正确姿势打开MySQL客户端命令行或Navicat/MySQL Workbench。如果用命令行先登录mysql -u root -p输入密码root如果你用的是Docker命令密码就是root。创建数据库如果还没建CREATE DATABASE doubao DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;切换数据库USE doubao;执行SQL脚本SOURCE /path/to/your/download/doubao.sql;把/path/to/...替换成你实际的路径。如果报错ERROR 1064大概率是脚本开头有BOM头或编码问题用VS Code打开doubao.sql右下角点“UTF-8”选“Save with Encoding” → “UTF-8”再保存一次。验证执行SELECT COUNT(*) FROM user;应该返回0因为脚本里只建表没插测试用户需要你注册执行SHOW TABLES;应该看到user,post,comment,like_record等表名。4.3 后端启动修改配置运行mvnw打开IDEA或VS Code打开项目根目录就是有pom.xml的那个文件夹。找到src/main/resources/application.yml检查数据库配置yaml spring: datasource: url: jdbc:mysql://localhost:3306/doubao?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root # 改成你MySQL的实际密码如果你用的是Dockerlocalhost要改成host.docker.internalMac/Windows或宿主机IPLinux。在IDEA里右键com.doubao.DoubaoApplication.java→Run DoubaoApplication或者在终端进入项目根目录执行bash ./mvnw spring-boot:run # Mac/Linux mvnw.cmd spring-boot:run # Windows等待控制台输出Started DoubaoApplication in X.XXX seconds并且最后一行是Tomcat started on port(s): 8080 (http)说明后端启动成功。此时访问http://localhost:8080/api/auth/test一个内置的测试接口应该返回{code:200,msg:test ok,data:null}。4.4 前端启动进入vue/目录npm install再npm run serve打开一个新的终端窗口不要关掉后端那个。进入vue/目录cd vue安装依赖npm install如果卡在node-sass可以先npm install node-sass4.14.1再npm installVue2 CLI 3.x兼容这个版本。启动前端npm run serve等待输出App running at: - Local: http://localhost:8081/说明前端已就绪。打开浏览器访问http://localhost:8081首页应该正常显示。点击右上角“注册”填入用户名、密码、邮箱提交。此时后端控制台会打印INSERT INTO user ...的日志数据库user表里多了一条记录。注册完用刚注册的账号登录。登录成功后前端会把token存入localStorage你可以按F12打开开发者工具 → Application → Local Storage →http://localhost:8081看到token字段。点击“发帖”填写标题和内容点击发布。打开Network面板找到POST /api/posts请求Headers里能看到Authorization: Bearer eyJhbGciOi...Preview里能看到返回的帖子ID。刷新页面帖子列表里就出现了你的新帖。整个流程走通意味着你已经完成了从数据库建模、后端API开发、前端页面渲染、JWT鉴权、跨域通信的全链路验证。这不是“Hello World”而是真实的社区功能闭环。5. 常见问题与排查技巧实录那些让我重启三次IDEA的坑现在都给你列明白了在帮十几个学员部署这个项目的过程中我整理了一份高频问题清单。这些问题90%都源于环境差异或操作细节疏忽而不是代码本身有Bug。我把它们按发生阶段分类并附上我的“秒级定位法”。5.1 后端启动失败Failed to configure a DataSource现象IDEA控制台一启动就报红核心错误是Consider defining a bean of type javax.sql.DataSource in your configuration.后面跟着一堆Caused by: java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因与解法-Driver类找不到pom.xml里mysql-connector-java依赖版本太低如5.1.47而MySQL 8.0要用8.0.33。打开pom.xml找到artifactIdmysql-connector-java/artifactId把version改成8.0.33然后右键项目 →Maven→Reload project。-数据库URL格式错误MySQL 8.0强制要求serverTimezone参数。检查application.yml里的url必须包含?serverTimezoneAsia/Shanghai且useSSLfalse如果没配SSL证书。-密码错误或数据库不存在application.yml里password写错了或者doubao数据库根本没创建。用MySQL客户端手动连一下mysql -u root -p -h localhost -P 3306 doubao能连上说明配置没问题。5.2 前端无法访问后端APICORS error现象前端页面空白F12看Console报Access to XMLHttpRequest at http://localhost:8080/api/auth/login from origin http://localhost:8081 has been blocked by CORS policy.原因与解法-后端跨域配置被覆盖SecurityConfig.java里http.cors()没开。在filterChain()方法里http.csrf().disable()下面加上.cors().configurationSource(corsConfigurationSource())并新增一个Bean方法java Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList(http://localhost:8081)); // 前端地址 configuration.setAllowedMethods(Arrays.asList(GET, POST, PUT, DELETE, OPTIONS)); configuration.setAllowCredentials(true); configuration.addAllowedOrigin(http://localhost:8081); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, configuration); return source; }-前端axios baseURL写错检查vue/src/main.jsaxios.defaults.baseURL必须是http://localhost:8080/api不能少http://也不能写成/api那是相对路径会变成http://localhost:8081/api请求发给了前端自己。5.3 登录成功但无法发帖403 Forbidden现象登录接口返回200token也存进了localStorage但点“发帖”按钮Network里POST /api/posts返回403。原因与解法-JWT Filter没生效检查SecurityConfig.java里addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)这行UsernamePasswordAuthenticationFilter.class必须拼写正确注意大小写且jwtAuthenticationFilter()方法返回的Bean必须是JwtAuthenticationFilter实例不能是new JwtAuthenticationFilter()那样就不是Spring管理的Bean了。-token过期或格式错误登录后立刻打开F12 → Application → Local Storage复制token值粘贴到https://jwt.io解码。看payload里exp过期时间是不是过去的时间戳看userId是不是数字而不是字符串123后端jwtUtil.getUserIdFromToken()方法里用了Long.parseLong()如果是字符串会抛异常Filter就静默失败了。5.4 页面样式错乱或组件不渲染现象首页加载出来但导航栏没了或者帖子列表是空白的Console里报[Vue warn]: Property or method posts is not defined on the instance。原因与解法-Vue CLI版本冲突vue/package.json里vue-cli-service版本如果是^4.5.0而你全局装了Vue CLI 5.x会导致构建失败。解决方案删除vue/node_modules和vue/package-lock.json然后npm install重新装确保装的是vue-cli-service4.5.15。-ESLint校验阻断vue/src/main.js里如果有console.log()ESLint会报错阻止编译。在vue/vue.config.js里加js module.exports { lintOnSave: false // 关闭保存时校验 }我把这些坑总结成一张速查表方便你随时对照问题现象最可能原因快速验证法修复命令/操作后端启动报DataSource错MySQL驱动版本不匹配mvn dependency:tree \| grep mysql改pom.xml里mysql-connector-java版本为8.0.33前端报CORS错误后端没开CORS或URL写错浏览器Network里看请求发到了哪个域名检查application.yml的url和main.js的baseURL登录后403发不了帖JWT Filter未注入Spring容器IDEA里CtrlClickjwtAuthenticationFilter()看能否跳转到定义确保Bean方法返回类型是JwtAuthenticationFilter页面空白Console报Vue warnvue-cli-service版本过高cd vue npm list vue-cli-service删除node_modulesnpm install重装实操心得每次遇到问题先做三件事1. 看后端控制台最后一屏日志不是最上面是启动完成后你操作时的日志2. 看前端Network面板里具体哪个请求失败、状态码多少、Response里是什么内容3. 打开F12的Console看有没有JS报错。90%的问题答案就在这三个地方。别一上来就怀疑代码有Bug先怀疑自己的环境和操作。6. 二次开发与能力延伸从“能跑”到“能改”这才是项目真正的价值现在你已经能让豆宝社区在本地跑起来了。但这只是起点。这个项目的真正价值在于它为你提供了一个“可触摸、可修改、可验证”的沙盒。接下来我分享几个我带学员做的、真正提升工程能力的实战练习每一个都紧扣关键词且都有明确的产出目标。6.1 给帖子加富文本编辑器Vue2 wangEditor实战原项目里发帖用的是纯textarea用户体验差。我们可以集成wangEditorVue2兼容版。步骤很简单1. 在vue/目录下npm install wangeditor4.7.12注意必须是4.x5.x只支持Vue3。2. 在components/CreatePost.vue里vue发布 3. 后端PostController.createPost()方法里RequestBody PostDTO dto的content字段接收的就是HTML字符串直接存入MySQL的content字段TEXT类型完全支持。 **收获**你学会了如何在Vue2项目里集成第三方UI组件理解了mounted/beforeDestroy生命周期的用途也实践了前后端对富文本的协同处理。6.2 实现帖子搜索功能MySQL全文索引 Spring Data JPA用户想搜“Vue教程”当前只能靠LIKE %Vue%效率低。我们可以用MySQL全文索引1. 在MySQL里执行sql ALTER TABLE post ADD FULLTEXT(title, content); -- 为标题和内容建全文索引2. 在后端PostRepository.java里加一个自定义查询方法java public interface PostRepository extends JpaRepositoryPost, Long { Query(value SELECT * FROM post WHERE MATCH(title, content) AGAINST(?1 IN NATURAL LANGUAGE MODE), nativeQuery true) ListPost searchByKeyword(String keyword); }3. 在PostService.java里暴露服务java public ListPost searchPosts(String keyword) { return postRepository.searchByKeyword(keyword); }4. 前端src/api/post.js里加searchPosts(keyword)方法components/SearchBar.vue里调用。收获你亲手给数据库加了索引写了原生SQL查询理解了MATCH...AGAINST和LIKE的本质区别前者用索引后者全表扫描也掌握了Spring Data JPA的Query用法。6.3 将JWT Token存入HttpOnly Cookie增强安全性目前token存在localStorage有XSS风险。我们可以改成存入HttpOnlyCookie1. 后端LoginController.login()里登录成功后不返回token而是java Cookie cookie new Cookie(AUTH_TOKEN, jwtUtil.generateToken(user)); cookie.setHttpOnly(true); cookie.setPath(/); cookie.setMaxAge(3600); // 1小时 response.addCookie(cookie);2. 前端main.js里axios请求拦截器改为从Cookie读token需引入js-cookie库javascript import Cookies from js-cookie axios.interceptors.request.use(config { const token Cookies.get(AUTH_TOKEN) if (token) { config.headers.Authorization Bearer ${token} } return config })收获你实践了Web安全的核心原则——敏感信息不存前端JS可访问的地方理解了HttpOnly的作用并完成了前后端Cookie传输的完整链路。这三个练习没有一个是“为了炫技”每一个都直指真实开发中的痛点编辑体验、搜索性能、安全加固。当你做完你就不再是一个“能跑Demo的人”而是一个“知道问题在哪、方案怎么选、代码怎么写、效果怎么验”的合格开发者。豆宝社区的价值正在于此——它是一块砖你拿它垫脚够得着更高的地方。本文还有配套的精品资源点击获取简介一套开箱即用的社区类应用源码前端用Vue 2.x实现页面交互与路由管理后端基于Spring Boot 2.x搭建RESTful接口数据库采用MySQL附带完整doubao.sql初始化脚本。项目结构规范包含标准Maven配置pom.xml、跨平台启动脚本mvnw/mvnw.cmd、JWT登录鉴权、用户注册登录、发帖、评论等核心社区功能。前后端代码物理分离前端位于独立vue目录后端Java代码集中在src/main/java下配套README.md说明本地运行步骤和基础配置方式。支持跨域访问适合用于学习前后端协作开发、接口调试、Spring Security权限控制、Vue组件通信及MySQL表结构设计无需修改即可在本地完成前后端联调与部署验证。本文还有配套的精品资源点击获取