ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SpringBoot+Vue旅游系统实战:RBAC权限与JWT认证闭环

SpringBoot+Vue旅游系统实战:RBAC权限与JWT认证闭环 简介这是一套面向计算机专业本科生的Java毕业设计实战资源聚焦旅游管理业务场景采用SpringBoot后端与Vue前端的主流技术栈开发适用于课程设计、期末大作业及高分毕设选题。资源包含857个文件涵盖139个Java核心业务类、51个Vue组件、164个JS交互逻辑、79个GIF动效资源及53个CSS样式文件另有SQL建表脚本、部署用BAT批处理、YML配置及完整项目结构文件压缩包仅24.16MB轻量易部署。已有237人学习下载资源附带万字详细报告文档、PPT答辩材料与分步部署说明代码含规范注释模块划分清晰——从用户/管理员双角色登录、景点与线路全生命周期管理到公告维护、轮播图配置及系统级数据备份功能闭环且贴近真实项目需求可直接运行并快速二次开发。1. 这不是又一个“登录增删改查”的Java毕设模板而是一套可直接跑通、带完整业务闭环的旅游管理系统实战工程你手头可能已经攒了三四个「SSM旅游系统」压缩包解压后发现前端页面全是静态HTML、后端Controller里连RequestBody都没用、数据库字段命名像“user_name”和“username”混着写、部署说明只有一句“导入数据库”。但这个项目不同——它用SpringBootVue双端分离架构IndexMain.vue.bak和update-password.vue.bak这类带.bak后缀的文件恰恰说明作者在开发过程中保留了原始版本用于回滚调试3-build.bat、2-run.bat、1-install.bat三个批处理脚本不是摆设而是真实覆盖了从环境初始化→服务启动→前端构建的全链路操作路径。它解决的不是“如何写DAO层”而是“如何让一个旅游信息平台在JDK1.8MySQL5.7环境下5分钟内完成本地验证”。适合正在赶毕设 deadline 的本科生、需要快速交付课程设计的专科生也适合想拿它当 SpringBoot Vue 联调范例来补全前后端协作认知的初级 Java 工程师——因为它的注释密度高关键方法含中文逻辑说明、模块边界清晰景点/线路/公告分库表不交叉、权限控制粒度到按钮级超级管理员 vs 普通管理员功能可见性差异明确。2. SpringBoot 后端核心模块拆解为什么选 MyBatis-Plus 而非纯 MyBatis以及 Controller 层如何规避空指针陷阱2.1 数据访问层选型依据MyBatis-Plus 提供的代码生成器与条件构造器是毕设提速关键该项目未使用 XML 手写 SQL全部基于 MyBatis-Plus 的QueryWrapper和LambdaQueryWrapper构建查询逻辑。例如在景点管理模块中ScenicSpotService.java中的列表查询方法public ListScenicSpot listByKeyword(String keyword) { LambdaQueryWrapperScenicSpot wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(keyword), ScenicSpot::getName, keyword) .or() .like(StringUtils.isNotBlank(keyword), ScenicSpot::getDescription, keyword); return this.list(wrapper); }提示StringUtils.isNotBlank(keyword)是关键防护点。若直接写wrapper.like(ScenicSpot::getName, keyword)当 keyword 为 null 时会触发 SQL 语法错误LIKE NULL不合法而此处先做空值校验再动态拼接条件符合生产级健壮性要求。这也是该毕设区别于“教学Demo”的重要标志——它把防御式编程落到了每一行 DAO 调用中。MyBatis-Plus 的AutoGenerator配置见generator/CodeGenerator.java其核心参数如下参数名值说明authoradmin生成代码作者标识便于后期追溯修改人outputDirsrc/main/javaJava 实体类、Mapper、Service 默认输出路径mapperXmlPathsrc/main/resources/mapperXML 映射文件存放位置本项目实际未启用XML仅作兼容预留tablePrefix[t_, sys_]自动剥离表名前缀生成实体类时字段名为name而非t_scenic_spot_name这种配置使开发者无需手动创建 POJO 类只需运行CodeGenerator.main()即可批量生成对应 MySQL 表结构的实体、Mapper 接口及 Service 实现类节省至少 2 小时重复劳动。2.2 权限控制实现RBAC 模型在 Spring Security 中的轻量级落地系统区分“超级管理员”与“普通管理员”并非靠角色字符串硬编码判断而是通过SysRole和SysUserRole两张关联表实现。关键逻辑位于SecurityConfig.java中的configure(HttpSecurity http)方法http.authorizeRequests() .antMatchers(/admin/**).hasAnyRole(ADMIN, SUPER_ADMIN) .antMatchers(/user/**).authenticated() .antMatchers(/login, /register, /static/**, /api/public/**).permitAll() .anyRequest().authenticated();但真正限制按钮级操作的是前端 Vue 的v-if指令配合后端接口返回的roleType字段。例如在IndexAsideStatic.vue.bak中el-menu-item v-ifuserInfo.roleType SUPER_ADMIN index/admin/scenic/add i classel-icon-plus/i span slottitle新增景点/span /el-menu-item注意该判断必须与后端接口权限校验双重保障。ScenicSpotController.add()方法顶部有PreAuthorize(hasRole(SUPER_ADMIN))注解确保即使前端绕过 v-if 发起请求也会被 Spring Security 拦截并返回 403。这是毕设中常被忽略的安全细节——很多学生只做前端隐藏后端无校验导致越权漏洞。2.3 登录认证流程JWT Token 生成与刷新机制解析项目未采用 Session 共享方案而是使用 JWT 实现无状态认证。LoginController.java中的login()方法核心逻辑如下// 1. 校验账号密码 User user userService.getByUsername(loginDTO.getUsername()); if (user null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) { throw new BusinessException(用户名或密码错误); } // 2. 生成 JWT Token有效期2小时 String token Jwts.builder() .setSubject(user.getUsername()) .claim(userId, user.getId()) .claim(roleType, user.getRoleType()) // 写入角色类型供前端菜单控制 .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 2 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, jwtSecret) .compact(); // 3. 返回 token 及用户基本信息 MapString, Object result new HashMap(); result.put(token, token); result.put(userInfo, user); return Result.success(result);JWT 密钥jwtSecret定义在application.yml中jwt: secret: tourism-system-2024-secret-key expire-time: 7200000 # 2小时毫秒值该密钥必须在部署时替换为强随机字符串否则存在 Token 伪造风险。项目提供的默认密钥仅用于本地调试不可用于正式环境。3. Vue 前端工程结构还原从.bak文件看开发迭代痕迹与路由守卫设计3.1.bak文件的真实用途版本回退与分支对比依据index.html.bak、update-password.vue.bak等文件并非冗余备份而是作者在重构过程中保留的历史版本。以update-password.vue.bak为例对比当前update-password.vue可发现原版使用this.$http.post(/api/user/updatePassword, {...})直接调用 axios新版改为import { updateUserPassword } from /api/user封装成独立 API 方法原版密码校验仅前端 JS 判断长度新版增加validatePasswordRule表单规则对象并联动后端/api/user/checkOldPassword接口做旧密码一致性验证。这说明项目经历过至少一次重大重构从“脚本式开发”转向“模块化API 分离”模式。对学习者而言比对.bak与现行文件能直观理解 Vue 工程化演进路径——比如BreadCrumbs.vue.bak中面包屑数据靠父组件传参而现行BreadCrumbs.vue改用this.$route.matched动态生成更符合 Vue Router 最佳实践。3.2 路由守卫实现登录态持久化与页面跳转拦截router/index.js中定义了全局前置守卫router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.meta.requiresAuth !token) { next(/login); } else if (to.path /login token) { next(/admin/dashboard); // 已登录用户访问登录页自动跳转首页 } else { next(); } });其中to.meta.requiresAuth在路由配置中显式声明{ path: /admin/scenic, name: ScenicList, component: () import(/views/admin/ScenicList.vue), meta: { requiresAuth: true } }提示localStorage.getItem(token)存在 XSS 风险但在毕设场景下可接受。若需提升安全性应改用httpOnly Cookie存储 Token并配合withCredentials: true设置 Axios 请求头。本项目未采用此方案因其增加了跨域调试复杂度不符合“快速部署”定位。3.3 组件通信模式EventBus 与 Vuex 的混合使用策略项目未引入完整 Vuex store但针对跨层级组件通信采用了轻量 EventBus 方案。utils/eventBus.js定义如下import Vue from vue; export const eventBus new Vue();在IndexHeader.vue.bak中监听用户登出事件created() { eventBus.$on(userLogout, () { this.$message.success(已退出登录); localStorage.removeItem(token); this.$router.push(/login); }); }而在IndexAsideStatic.vue.bak的退出按钮中触发el-submenu indexexit template slottitle i classel-icon-switch-button/i span退出系统/span /template el-menu-item clickhandleLogout安全退出/el-menu-item /el-submenumethods: { handleLogout() { eventBus.$emit(userLogout); } }这种设计避免了为简单场景引入 Vuex 的重量级依赖同时保证了组件解耦。对于毕设而言比“强行套用 Vuex”更体现工程权衡能力。4. 本地部署三步法从1-install.bat到3-build.bat的执行逻辑与常见报错排查4.1 批处理脚本逐行解析每个命令背后的系统依赖检查1-install.bat是环境初始化脚本核心内容如下echo off echo 正在检查 JDK 版本... java -version 21 | findstr 1.8 nul if %errorlevel% neq 0 ( echo 错误JDK 版本不是 1.8请安装 JDK 1.8 并配置 JAVA_HOME 环境变量 pause exit /b 1 ) echo 正在检查 MySQL 服务... netstat -ano | findstr :3306 nul if %errorlevel% neq 0 ( echo 错误MySQL 未启动或端口 3306 被占用 pause exit /b 1 ) echo 正在初始化数据库... mysql -u root -p123456 database/tourism_system.sql if %errorlevel% neq 0 ( echo 数据库导入失败请检查 root 密码是否为 123456 或手动执行 SQL 文件 pause exit /b 1 ) echo 数据库初始化完成 pause注意脚本中 MySQL 默认密码为123456实际部署时需在application.yml中同步修改spring: datasource: url: jdbc:mysql://localhost:3306/tourism_system?useUnicodetruecharacterEncodingutf8serverTimezoneGMT%2B8 username: root password: 123456 # 此处必须与 bat 脚本中一致4.22-run.bat启动服务的关键参数与端口冲突处理2-run.bat内容精简但关键echo off echo 正在启动后端服务... cd /d %~dp0.. mvn clean compile exec:java -Dexec.mainClasscom.tourism.Application -Dexec.args-Dserver.port8080 pause此处-Dexec.args-Dserver.port8080显式指定 SpringBoot 端口避免与本地其他 Java 应用冲突。若提示Address already in use: bind可临时修改为8081并同步更新前端vue.config.js中的代理配置devServer: { proxy: { /api: { target: http://localhost:8081, // 此处端口需与后端一致 changeOrigin: true, pathRewrite: { ^/api: } } } }4.33-build.bat构建静态资源为何必须先npm install再npm run build3-build.bat执行顺序不可颠倒echo off cd /d %~dp0../frontend echo 正在安装前端依赖... npm install --registryhttps://registry.npm.taobao.org if %errorlevel% neq 0 ( echo npm install 失败请检查网络连接或更换镜像源 pause exit /b 1 ) echo 正在构建前端生产包... npm run build if %errorlevel% neq 0 ( echo 构建失败请检查 package.json 中 scripts.build 命令是否为 vue-cli-service build pause exit /b 1 ) echo 构建完成dist 目录已生成可部署至 Nginx 或 Tomcat pause关键点在于npm install必须成功否则npm run build会因缺少vue-cli-service报错command not found。项目package.json中scripts部分定义如下scripts: { serve: vue-cli-service serve, build: vue-cli-service build, lint: vue-cli-service lint }若本地未全局安装vue/cli-service则必须依赖node_modules/.bin/vue-cli-service而这正由npm install创建。5. 功能模块验证技巧用 Postman 快速测试接口、用 Chrome DevTools 审查 Vue 组件状态5.1 接口测试清单五个必测核心接口及其预期响应使用 Postman 测试时建议按以下顺序验证每个接口均需携带Authorization: Bearer token请求头接口地址HTTP 方法请求参数预期响应码关键断言点POST /api/user/loginPOST{username:admin,password:123456}200data.token字段存在且非空GET /api/scenic/list?page1limit10GET无200data.list为数组且长度 ≥ 0POST /api/scenic/addPOST{name:西湖,description:杭州著名景点}200data.id返回新插入记录主键PUT /api/user/updatePasswordPUT{oldPassword:123456,newPassword:654321}200再次用新密码登录应成功DELETE /api/scenic/delete?id1DELETEURL 参数id200再次查询/api/scenic/list应不包含 id1 的记录提示若POST /api/user/login返回 500大概率是数据库连接失败。检查application.yml中spring.datasource.url是否正确指向本地 MySQL 实例且tourism_system库已存在由1-install.bat创建。5.2 Vue 组件状态调试利用 Vue Devtools 查看userInfo响应式数据流在 Chrome 中打开http://localhost:8080按 F12 打开开发者工具切换到Vue标签页需提前安装 Vue Devtools 插件。展开左侧组件树点击IndexHeader.vue右侧Data区域将显示userInfo: { id: 1, username: admin, roleType: SUPER_ADMIN, avatar: /static/avatar.jpg }若userInfo为空说明登录状态未正确注入。此时检查main.js中是否执行了store.dispatch(setUserInfo, res.data.userInfo)本项目实际未用 Vuex故应检查localStorage.setItem(userInfo, JSON.stringify(res.data.userInfo))是否被执行。5.3 数据库字段映射验证当页面显示“undefined”时的三步定位法若景点列表中名称显示为undefined按以下顺序排查查前端模板绑定打开ScenicList.vue确认div{{ item.name }}/div中的name是否与后端返回 JSON 字段一致查后端 DTO 定义查看ScenicSpot.java实体类确认private String name;及其 getter/setter 是否存在查 SQL 查询语句进入ScenicSpotMapper.xml若存在或ScenicSpotMapper.java中的selectList方法确认SELECT * FROM t_scenic_spot是否包含name字段或是否用了Select(SELECT id,name,description FROM t_scenic_spot)显式指定列。本项目采用 MyBatis-Plus 自动生成 SQL因此第 3 步通常无需干预问题多出在第 1 或第 2 步——比如实体类字段名为scenicName而前端模板仍写item.name就会导致 undefined。6. 毕设答辩加分项PPT 中如何呈现技术深度而非界面截图6.1 三页核心 PPT 结构用架构图替代功能列表用对比表格替代文字描述答辩 PPT 切忌堆砌“系统首页截图”“景点列表截图”“用户管理截图”。应聚焦技术决策点例如第一页技术选型对比表维度本方案SpringBootVue传统方案JSPServlet优势说明开发效率前后端分离API 接口复用率高前端逻辑嵌入 Java 代码修改成本高毕设周期压缩 40%权限控制Spring Security JWT支持按钮级控制Filter 拦截 URL仅能控制页面级访问体现 RBAC 实战能力部署便捷性3-build.bat一键生成 distNginx 静态托管WAR 包需 Tomcat 容器配置复杂符合企业级交付标准第二页关键流程时序图手绘风格更佳用户输入账号密码 ↓ LoginController.login() → 校验密码 → 生成 JWT Token ↓ 前端 localStorage 存储 Token ↓ Axios 请求拦截器自动添加 Authorization 头 ↓ ScenicSpotController.list() → SecurityContext 获取当前用户 → 查询数据库此图无需 Visio 制作用 PPT 形状工具绘制箭头文字即可重点展示“Token 如何贯穿全流程”。第三页部署问题解决实录附终端截图截取1-install.bat执行失败时的 CMD 窗口红框标出MySQL 未启动错误并配文字“解决方案以管理员身份运行services.msc启动 MySQL80 服务”。真实问题比完美结果更有说服力。6.2 万字报告文档使用指南如何提取有效论据支撑“系统设计合理性”项目附带的万字报告不是用来全文背诵的而是作为答辩问答弹药库。例如当老师问“为什么用 MyBatis-Plus 而不用 JPA”——直接翻到报告第 3.2 节《持久层框架选型分析》引用原文“JPA 的 Hibernate 实现对复杂关联查询支持较弱且在 MySQL 5.7 环境下需额外配置方言而 MyBatis-Plus 提供的LambdaQueryWrapper可在编译期校验字段名避免运行时 SQL 异常更适合本科毕设对稳定性的要求。”这种引用方式既体现你读过报告又展现批判性思维——不是“因为大家都用所以我也用”而是“因为特定约束条件所以选择此方案”。6.3 源码注释价值挖掘用param和return注释反向推导业务规则例如ScenicSpotService.java中listByKeyword()方法的注释/** * 根据关键词模糊查询景点列表支持按名称和描述两个字段匹配 * param keyword 搜索关键词为空时返回全部景点 * return 匹配的景点列表按创建时间倒序排列 */ public ListScenicSpot listByKeyword(String keyword) { ... }答辩时可主动指出“老师您看这里的param明确说明了空值处理逻辑return规定了排序规则——这说明我们在设计阶段就考虑了用户体验空搜索返回全部和业务需求最新景点优先展示不是盲目编码。”这种对注释的深度解读远胜于复述“我写了 XXX 行代码”。本文还有配套的精品资源点击获取
返回列表