简介面向Java毕业设计与课程设计的综合性实战资源基于SSM框架与Vue.js构建家乡特产网上商城采用B/S架构搭配MySQL数据库覆盖前端页面、后端业务逻辑与数据库表结构适合需要完整项目源码、演示与说明材料的高校学生参考。资源共803个文件包含114个Java源码、60个Vue组件、159个JavaScript脚本、38个HTML页面及51个样式表另附SQL数据库脚本、演示视频、项目说明及运行辅助脚本整套压缩包约42.72MB目录结构清晰便于按模块研读。已有125人浏览学习适合作为毕业设计选题时的直接参照。可获取完整前端商城页面与后端接口实现、管理员与用户双视角操作演示录屏、数据库初始化脚本及搭建说明等帮助快速理解SSMVue在真实商城场景中的整合方式有效缩短设计与开发周期。1. 从课程设计到答辩演示这个 SSM 家乡特产商城能直接改着用如果你正在找一份能跑通、能讲清楚、能自己改的 Java 毕业设计那基于 SSM 框架的家乡特产网上商城源码包可能是你今晚就能用上的东西。它用 Spring SpringMVC MyBatis 搭了一个完整的前后台商城前台有商品浏览、购物车、下单后台有商品管理、订单管理、用户管理还带演示视频和一份能照着做的说明文档。对课程设计、毕业设计来说它解决的不只是“有代码”而是让你从启动项目到答辩讲原理都有一条清晰的路。适合已经会 JDK、MySQL、Maven 基础操作但没时间从零搭商城的人。下面我会把它的结构和启动细节拆给你看。2. 系统拆解权限设计、数据库表与 SSM 目录结构2.1 三类角色与功能模块边界这个商城的业务边界很清晰按角色分成游客、注册用户、管理员三套操作逻辑。游客看商品分类、商品列表、商品详情不需要登录。注册用户登录后能加入购物车、提交订单、查看自己的订单、维护个人资料。管理员登录后台后管理分类、商品上下架、订单发货和用户状态。从源码的 Controller 命名就能看出这种边界。ProductController 管前台商品CartController 管购物车OrderController 管订单AdminProductController 和 AdminOrderController 管后台。每个 Controller 只处理自己职责内的请求答辩时你直接说“订单相关的请求都进 OrderController”评委就能理解你的分层思路。我见过不少课程设计把所有接口堆在一个 Controller 里虽然也能跑但写 service 层时根本没法维护。权限控制不是靠 AOP 或 Shiro 那种重框架而是靠 SpringMVC 拦截器加 Session 完成的。登录成功后把用户对象放进 Session后台路径再单独配一个管理员校验。这样最简单也最容易跟评委解释。后面第 4 章我会把拦截器代码贴出来。2.2 表结构设计订单状态机与价格快照数据库是这个项目里最值得讲的部分。核心表有五张用户表、分类表、商品表、订单表、订单项表。先看用户表。CREATE TABLE tb_user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT MD5 加密后密码, nickname VARCHAR(50) DEFAULT NULL, phone VARCHAR(20) DEFAULT NULL, role TINYINT DEFAULT 0 COMMENT 0-普通用户 1-管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里用 role 字段区分用户和管理员而不是单独建管理员表。对课程设计来说一张用户表加一个角色字段足够还能少维护一套登录逻辑。密码不要明文存资源里用了 MD5 加密虽然强度一般但比明文好太多。如果你想让答辩更有亮点可以在原有基础上改成 MD5 加盐改动很小。商品表要特别注意价格和库存字段。CREATE TABLE tb_product ( id INT NOT NULL AUTO_INCREMENT, category_id INT NOT NULL, name VARCHAR(100) NOT NULL, origin VARCHAR(100) DEFAULT COMMENT 产地比如福建宁德, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image_url VARCHAR(255) DEFAULT , description TEXT, status TINYINT DEFAULT 1 COMMENT 1-上架 0-下架, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;价格字段用 DECIMAL(10,2) 而不是 DOUBLE这是为了避免浮点误差。商品不上架不是删除记录而是把 status 改成 0这样历史订单里还能关联到商品信息。origin 字段是“家乡特产”这个主题留下来的业务痕迹你可以把它改成自己的家乡地名整个系统就变成你的作品了。订单表是状态流转的核心。CREATE TABLE tb_order ( id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, order_no VARCHAR(32) NOT NULL COMMENT 业务单号, total_amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-待付款 1-已付款待发货 2-已发货 3-已签收 4-已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;订单状态定义成一个 0~4 的整数比字符串好存储好比较。我给你一个状态机的判断逻辑待付款可以转已付款待发货也可以转已取消已付款待发货只能转已发货已发货只能转已签收。这个流转规则在代码里用常量类或者枚举体现而不是散落的 if 判断。订单项表是整个库设计里最容易加分的地方。CREATE TABLE tb_order_item ( id INT NOT NULL AUTO_INCREMENT, order_id INT NOT NULL, product_id INT NOT NULL, product_name VARCHAR(100) NOT NULL COMMENT 冗余商品名, price DECIMAL(10,2) NOT NULL COMMENT 快照价格, quantity INT NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表把商品名称和价格直接复制了一份而不是每次下单都去 join 商品表。为什么因为用户下单后商品可能改价甚至下架订单明细必须保留用户下单那一刻的商品名和价格。这叫“数据快照”在企业订单系统里是标配。答辩时能说出这一点评委对你的印象会不一样。2.3 目录结构为什么源码看起来乱但其实是标准三层拿到源码后的第一件事先看 src 目录长什么样。src/main/java/com/shop/ ├── controller/ # 控制器层 ├── service/ # 业务接口 ├── service/impl/ # 业务实现 ├── mapper/ # MyBatis Mapper 接口 ├── pojo/ # 实体类与 VO/DTO └── config/ # Spring 配置类、拦截器配置controller 层只接收参数和返回视图service 层写业务规则mapper 层做数据库交互。很多同学拿到别人源码后喜欢直接改 controller把 SQL 写在 controller 里这是毕设翻车最严重的问题——一旦功能逻辑复杂代码根本没法调试。你要坚持让 service 层承担主要业务controller 越薄越好。资源里还有 resources 目录下面放着 jdbc.properties、spring-mvc.xml、spring-mybatis.xml、mybatis-config.xml 等配置文件。有人会觉得 XML 配置繁琐但 SSM 课程设计就是要你用 XML 把“容器管理哪些包、拦截哪些路径”讲清楚这就是教学目的。2.4 选型理由SSM 在毕设场景下的不可替代性现在很多新项目都转向 Spring Boot但高校课程设计和毕业设计里 SSM 依然占很大比例。原因是 SSM 的 XML 配置是显式写出来的配置类扫描包、配置拦截器、配置数据源每一步都见得到。Spring Boot 则是自动配置很多细节是黑匣子你很难跟评委解释“为什么写一个 starter 就能跑”。所以这份资源选型 SSM 不是落后而是教学场景下的匹配选择。如果你时间富余可以在跑通后把配置迁移到 Spring Boot但我不建议为了“新”硬改。课程设计答辩重点是你对 MVC 流程、MyBatis 映射、事务控制的理解SSM 反而更容易展示这些基础原理。把这个项目跑透前后台流程走一遍那些框架之间的协作关系你就清楚了。3. 环境搭建JDK、MySQL、Maven 到 Tomcat 的启动全流程3.1 版本组合与核心配置文件SSM 项目最大的坑是版本不匹配。资源说明文档里如果写了版本严格按说明来。如果没有写我建议用下面这套兼容性最好的组合组件常见可选版本说明JDK1.8SSM 稳定运行的基础版本MySQL5.7 / 8.05.7 兼容性最好8.0 需要换驱动Maven3.6.x依赖管理Tomcat8.5支持 Servlet 3.1Spring5.x与 JDK 1.8 搭配连接数据库的配置在 jdbc.properties 里名字可能略有不同但核心字段是一致的jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456参数说明useUnicode 和 characterEncoding 保证中文不乱码serverTimezone 是 MySQL 8 驱动强制要求的时区参数。如果你使用 MySQL 5.7但是驱动版本也很老这个参数反而可能报错。所以拿到源码后先确认本地 MySQL 版本再决定 url 里留不留 serverTimezone。这个问题每年都有人踩。Spring 的数据源配置通常在 spring-mybatis.xml 或 applicationContext.xml 里bean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /beanDriverManagerDataSource 是 Spring 自带的简易数据源适合课程设计性能和连接池都比不上 Druid。如果你想让项目更像企业级可以把 class 换成 com.alibaba.druid.pool.DruidDataSource再引入 Druid 依赖代码层面改动只有这一个 bean。我一般会保留简易版因为评委看的是 MyBatis 和 Spring 的整合逻辑不在数据源上纠结。3.2 数据库初始化与 Maven 打包第一次启动前数据库需要手动创建。说明文档里一般会提到一个 sql 文件路径通常在 resources/sql/ 下。mysql -uroot -p -e CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4; mysql -uroot -p shop src/main/resources/sql/shop.sql这里有几个参数要理解第一个命令创建数据库指定 utf8mb4 字符集第二个命令把 sql 文件导入 shop 库。注意导入时终端要能识别 符号Windows 的 cmd 也可以直接用这个语法。如果你用的是 IDEA 的数据库工具也可以打开 sql 文件直接执行。接下来是 Maven 打包。源码通常是一个完整工程在项目根目录执行mvn clean package -DskipTestsskipTests 表示跳过测试用例毕设项目一般没有单元测试但这个参数能避免因为测试类报错导致打包失败。打包完成后 target 目录下会生成 war 包把这个 war 丢到 Tomcat 的 webapps 目录再启动 Tomcat 就完成部署了。如果你用的是 IDEA可以直接配置 Tomcat Server把部署工件选成 war exploded这能省去每次改代码重新打包的时间。3.3 IDEA 部署与启动排查IDEA 里部署 SSM 项目有一个单独的 Artifacts 配置过程。打开 Project Structure添加一个 Web Application Exploded把依赖的 lib 加进去然后配置 Tomcat 的 Deployment。如果你第一次接触这个流程建议直接看演示视频里 IDEA 部署那一段比文字描述直观得多。视频里通常会演示从导入项目到浏览器打开首页的全过程照着做一次就能跑起来。启动 Tomcat 后访问路径要注意 context path。如果 war 包名称是 shop.war访问地址一般是http://localhost:8080/shop/如果你看到 404先不要怀疑代码去 Tomcat 的 webapps 目录确认 war 是否解压成 shop 文件夹。Tomcat 解压失败的原因很多比如 web.xml 里 spring 配置文件路径写错或者 jar 包冲突。此时打开 Tomcat 日志也就是 catalina.out 或 console 标签页看有没有 Exception。3.4 项目里前端资源与 Vue 引入方式资源里前端页面不是纯 JSP而是引入了 Vue.js。常见做法是在 JSP 页面底部用 CDN 引入 Vue 文件script srchttps://cdn.jsdelivr.net/npm/vue2/dist/vue.js/script script src${pageContext.request.contextPath}/js/product.js/script如果你的电脑没有外网CDN 文件加载不出来页面就完全没有交互效果。最简单的解决办法是把 vue.js 文件下载下来放到 webapp/js 目录改成本地路径。很多同学卡在这里页面能打开但列表不渲染第一反应是 JS 代码错了其实只是 CDN 没加载成功。你把 url 复制到浏览器单独访问一次如果打不开那就是网络问题。4. 核心代码登录鉴权、购物车与订单状态机的实现4.1 登录鉴权拦截器与 Session 线程绑定登录功能在 SSM 项目里的标准实现是 HandlerInterceptor。用户登录成功后把用户信息存进 Session拦截器在请求到达 Controller 之前检查 Session。这里是一个最简洁的拦截器写法public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; } }逻辑说明preHandle 在 Controller 方法执行前运行。user 为 null 说明没有登录sendRedirect 跳到登录页return false 终止后续执行。user 存在就 return true 放行。在 SpringMVC 配置文件里注册这个拦截器并指定放行路径mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/register/ mvc:exclude-mapping path/product/**/ mvc:exclude-mapping path/static/**/ bean classcom.shop.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptors参数说明mapping path/** 表示拦截所有路径exclude-mapping 是白名单product 下商品列表和详情必须放行因为游客也要看商品静态资源放行是避免 CSS、JS 被拦截后页面样式丢失。你拿到的源码里可能还多一个 admin 的 exclude 配置那是给后台登录页用的。这个方案在答辩时很好讲但要留意一个体验问题如果你用 Ajax 请求登录过期后后端返回的不是 JSON而是重定向指令前端 Vue 会拿不到数据。如果你想做得更完善可以判断请求头里是否带 X-Requested-With但那是进阶内容能说出来也是加分项。4.2 购物车Cookie 方案与数据库方案的选择购物车这部分的实现不同源码包差别很大。我见过这个类型资源里有几种思路有的是登录后才能加购物车用表存储有的是用 Cookie 存 JSON游客也能加。两种各有道理如果你拿到的是 Cookie 版本核心逻辑大概是这样的private void addToCart(HttpServletRequest request, HttpServletResponse response, Integer productId, Integer quantity) { Cookie[] cookies request.getCookies(); ListCartItem cart new ArrayList(); if (cookies ! null) { for (Cookie cookie : cookies) { if (cart.equals(cookie.getName())) { cart JSON.parseArray(cookie.getValue(), CartItem.class); break; } } } boolean exists false; for (CartItem item : cart) { if (item.getProductId().equals(productId)) { item.setQuantity(item.getQuantity() quantity); exists true; break; } } if (!exists) { cart.add(new CartItem(productId, quantity)); } Cookie cookie new Cookie(cart, JSON.toJSONString(cart)); cookie.setMaxAge(7 * 24 * 3600); cookie.setPath(/); response.addCookie(cookie); }逻辑说明先从 request 里取出名为 cart 的 Cookie解析成购物车列表然后遍历列表如果商品已存在就累加数量不存在就新增最后把列表写回 Cookie设置七天有效期。Cookie 的 path 要设置成 /否则只在当前路径下有效换一个页面就读不到。Cookie 方案的好处是未登录也能加购物车后端少维护一张表坏处是换设备购物车不同步。如果你希望答辩更有商业系统逻辑可以在登录后把购物车同步到数据库。具体做法是建一张 tb_cart 表字段为 user_id、product_id、quantity加唯一索引 user_id product_id。登录成功后从 Cookie 读一次并批量写入数据库然后清掉 Cookie。这个改进能讲十分钟。4.3 订单状态机与事务边界订单创建是 SSM 项目里最需要谨慎的地方因为它涉及多张表的数据一致性。一次下单操作要写订单表和订单项表还要扣库存。这里必须用事务。Service public class OrderServiceImpl implements OrderService { Transactional Override public Order createOrder(Integer userId, ListCartItem items) { // 1. 生成订单主表status 置为 0 待付款 // 2. 遍历 items 写入 order_item 表 // 3. 扣减商品库存库存不足则抛异常触发回滚 // 4. 清空用户购物车 return order; } }逻辑说明Transactional 声明事务边界方法内任何 RuntimeException 抛出都会让前面所有数据库操作回滚。库存扣减这一步在 SQL 里就应该带上 stock 0 的条件否则并发场景会出现超卖。UPDATE tb_product SET stock stock - #{quantity} WHERE id #{productId} AND stock #{quantity}这里影响行数为 0 表示库存不足Java 层拿到这个结果后抛异常。为什么要用一行 SQL 而不是先查库存再扣减因为“先查再改”在并发下会出现两个请求同时读到库存 1然后都执行扣减最后库存变成 -1。用条件更新可以在数据库层面挡住超卖。你能在答辩时说出这条订单设计的分数就跑不掉了。订单状态流转建议用一个状态判断方法public boolean canTransit(Integer from, Integer to) { return switch (from) { case 0 - to 1 || to 4; case 1 - to 2; case 2 - to 3; default - false; }; }这个 switch 表达式是 Java 14 的语法源码如果是 JDK 1.8 就换成 if-else。它的作用是把状态流转规则集中在一个方法里避免在 Controller 里到处写 if (status 0 action 1)。状态流转集中管理后后台订单管理的每个按钮只需调用这个方法判断是否能操作。4.4 MyBatis 分页与商品列表查询商品列表必然涉及分页SSM 里最常见的是 PageHelper 插件。在 Maven 依赖里加入dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper/artifactId version5.3.0/version /dependency然后在 spring-mybatis.xml 里配置插件property nameplugins array bean classcom.github.pagehelper.PageInterceptor property nameproperties valuehelperDialectmysql/value /property /bean /array /property参数说明helperDialect 告诉插件目标数据库类型是 MySQLPageHelper 会拦截接下来执行的查询 SQL 并自动拼接 limit 语句。Mapper 接口方法不需要传 limit 参数而是在 Service 层调用 PageHelper.startPage(pageNum, pageSize) 后紧跟着查询方法。public ListProduct listByCategory(Integer categoryId, int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return productMapper.selectByCategory(categoryId); }这里有个使用规范PageHelper.startPage 后面必须紧跟第一条查询语句中间不能插入其他 SQL否则分页会作用到错误的查询上。这是一个高频翻车点我在第 5 章会专门提。4.5 Vue 局部渲染与后端接口返回资源里的前台页面用 Vue 做列表和购物车小红点刷新。前端页面初始化时通过 axios 请求后端接口拿到 JSON 数组渲染到页面。new Vue({ el: #app, data: { productList: [] }, created() { this.loadProducts(); }, methods: { loadProducts() { axios.get(/shop/product/list) .then(res { this.productList res.data; }); } } });对应的后端 Controller 要返回对象而不是页面需要加 ResponseBodyRequestMapping(/list) ResponseBody public ListProduct list(RequestParam Integer categoryId) { return productService.listByCategory(categoryId, 1, 10); }注意这里的路径axios.get(/shop/product/list)开头是 /shop这是 context path。如果你部署时改了 war 包名称这段 axios 里的前缀也要同步改否则请求会 404。很多人把精力放在后端调试忽略了前端 JS 里的路径和部署环境耦合这是前端集成 SSM 最常见的坑。在 SpringMVC 配置里还要记得加注解驱动mvc:annotation-driven/没有这个标签ResponseBody 不会生效接口会直接返回 ModelAndView 的视图名前端拿到的是字符串而不是 JSON。这个问题属于“配置缺失到怀疑人生”的类型且这类配置通常在 xml 里不看日志很难发现。5. 避坑排查SSM 毕业设计最常见的 5 个翻车点5.1 中文乱码JSP 页面和 MySQL 双端不一致现象商品名称在页面显示成问号或者插入数据库后变成??。 原因Tomcat 接收请求时默认字符集不是 UTF-8MySQL 连接串没带 characterEncoding或者建表时用了默认 latin1。 解决统一字符集。jdbc.properties 里加 useUnicodetruecharacterEncodingutf8JSP 页面头部加 pageEncodingUTF-8建库时指定 utf8mb4。如果库已经建好执行 ALTER DATABASE shop CHARACTER SET utf8mb4; 再把表也改掉ALTER TABLE tb_product CONVERT TO CHARACTER SET utf8mb4;这个转换命令会重建表表数据量小的时候没影响。改完后建议重启 Tomcat 再试因为连接连接池里的旧连接可能还保持着老编码。5.2 连接 MySQL 8 报错驱动类找不到或时区异常现象启动报ClassNotFound: com.mysql.jdbc.Driver或者The server time zone value is unrecognized。 原因MySQL 8.0 开始驱动包名改成com.mysql.cj.jdbc.Driver而且连接必须指定 serverTimezone。 解决在 pom.xml 或 lib 目录里确认驱动包是 mysql-connector-java 8.x然后把配置改成jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai如果你的 MySQL 是 5.7驱动是 5.1.x就不要加 serverTimezone否则老驱动会报参数不支持。判断依据是看项目里 lib 文件夹下驱动的 jar 版本不看 MySQL 服务端版本。5.3 页面样式全丢拦截器把静态资源拦截了现象登录后页面能开但 CSS、JS 和 Vue 文件全部 404前端样式完全错乱。 原因拦截器配置了/**拦截所有请求但没有放行 static 目录SpringMVC 也没配置静态资源映射。 解决先加静态资源映射mvc:resources location/static/ mapping/static/**/然后在拦截器 exclude 里放行 staticmvc:exclude-mapping path/static/**/如果页面里的资源路径不是/static/开头而是css/那就把 location 和 mapping 改成对应路径。这个问题的排查方法很简单看浏览器开发者工具里报 404 的资源路径反推项目里实际存放位置再改配置。5.4 MyBatis 查询结果字段全为 null现象SQL 在数据库工具里执行有值但页面显示商品价格是 0.0名称是 null。 原因数据库字段是下划线命名 user_idJava 属性是驼峰 userIdMyBatis 默认不会自动映射。 解决在 mybatis-config.xml 里开驼峰映射settings setting namemapUnderscoreToCamelCase valuetrue/ /settings开了这个开关之后绝大多数下划线字段能自动对应到驼峰属性。如果你用了SELECT *之外的写法给字段起别名比如user_id AS uid映射又会失效。因为别名直接覆盖了字段名MyBatis 从结果集取列名时只能看到 uid找不到对应的 userId 属性。所以别随手写别名。5.5 PageHelper 分页错乱或丢失 order by现象第二页数据还是第一页的内容或者分页时排序失效。 原因PageHelper.startPage 后不是立即查询中间执行了其他 SQL导致分页插件作用到错误查询上。 解决强制要求 startPage 和下一行必须紧跟 Mapper 查询PageHelper.startPage(pageNum, pageSize); return productMapper.selectByCategory(categoryId);如果你在 startPage 和 select 之间写了一条日志 INSERT 操作这条日志 SQL 会被分页页面上数据就全乱了。我一般还会在 Controller 里把分页结果封装成 PageInfo这样前端能拿到 total 这个关键字段PageInfoProduct pageInfo new PageInfo(productList);PageInfo 里包含总页数和当前页比直接返回 List 更适合前端渲染分页条。这个封装没有额外成本答辩时还能顺带讲一讲插件原理。6. 进阶验证SSM 后端挂 Vue 前端跨域配置与接口连通性检查如果你想把这份源码改得更有企业味最直接的一个方向是把前端页面和服务端拆成两个独立端口运行。SSM 后端仍然提供接口Vue 页面单独部署在另一个端口这样前后端通过 Ajax 通信就会碰到跨域问题。SpringMVC 里可以用一个配置类解决Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:8081) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true); } }参数说明allowedOrigins 写前端页面的完整访问来源/api/**表示只对接口路径开放跨域allowCredentials(true) 允许携带 Cookie这样 Session 登录状态在跨域下依然有效。如果不打开 credentialsaxios 默认不会把 Cookie 带过去你会发现跨域能访问但接口提示未登录。配置好后用 curl 验证接口连通性是最快的。先登录拿 Session Cookie再访问商品列表curl -c cookie.txt -d usernameadminpassword123456 http://localhost:8080/shop/admin/login curl -b cookie.txt http://localhost:8080/shop/api/product/list第一条命令把响应里的 Set-Cookie 保存到 cookie.txt第二条命令带上这个 Cookie 请求接口返回 JSON 就说明登录链路和接口都正常。没有 curl 的话用浏览器开发者工具同样能看到请求头里的 Cookie 和响应 JSON。这个验证方法是我拿到每一份 SSM 源码后必做的第一件事。跑通登录-访问主流程只需要五分钟却能筛掉大量隐藏问题。从那以后我每次遇到新源码都会强制自己先走一遍 curl 的登录和列表请求确认后端链路通畅之后才去调前端页面。这套检查思路对课程设计答辩前的排错尤其有用希望帮到你。本文还有配套的精品资源点击获取