# SpringBoot集成SpringDoc OpenAPI(Swagger)企业级实战教程
本文以企业级开发标准为基准,从零开始讲解SpringBoot 3.5.10集成SpringDoc OpenAPI(适配SpringBoot 3.x,替代传统Swagger2)的完整流程,修复接口扫描冲突、整合完整JWT认证体系、实现用户/订单双模块开发,涵盖基础配置、高级定制、JWT完整集成、接口测试、生产环境管控等核心实战要点,所有代码可直接复制运行,适配实际企业开发场景。
# 1. 开发环境确认
确保开发环境满足以下标准化配置(SpringBoot 3.x强制要求):
- IDE:IntelliJ IDEA 2023+(Ultimate/Community版均可)
- JDK:17(SpringBoot 3.x最低兼容JDK 17,推荐17/21)
- SpringBoot:3.5.10
- Maven:3.9.4+
- SpringDoc OpenAPI:2.6.0(适配SpringBoot 3.5.10的最新稳定版,兼容度最高)
- JJWT:0.12.5(SpringBoot 3.x适配的JWT工具,实现接口认证)
# 2. 项目初始化
新建SpringBoot项目

企业级标准项目结构
初始化后按以下结构整理(补充JWT、多模块相关包,解决后续扫描/依赖问题):
SpringBoot-Swagger/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── cc/
│ │ │ └── hep/
│ │ │ ├── SpringBootSwaggerApplication.java // 项目启动类
│ │ │ ├── common/ // 通用工具包
│ │ │ │ └── utils/ // 工具类子包
│ │ │ │ └── JwtUtil.java // JWT核心工具类(生成/解析/校验)
│ │ │ ├── config/ // 核心配置包
│ │ │ │ ├── OpenApiConfig.java // SpringDoc核心配置(分组+JWT+全局信息)
│ │ │ │ ├── WebMvcConfig.java // Web配置(注册JWT拦截器)
│ │ │ │ └── interceptor/ // 拦截器子包
│ │ │ │ └── JwtInterceptor.java // JWT全局拦截器(接口认证)
│ │ │ ├── controller/ // 控制器包(多业务模块)
│ │ │ │ ├── UserController.java // 用户模块控制器(含登录接口)
│ │ │ │ └── OrderController.java // 订单模块控制器
│ │ │ ├── model/ // 实体类包
│ │ │ │ ├── User.java // 用户实体(敏感字段隐藏)
│ │ │ │ └── Order.java // 订单实体
│ │ │ └── service/ // 业务层包
│ │ │ ├── UserService.java // 用户业务接口
│ │ │ ├── OrderService.java // 订单业务接口
│ │ │ └── impl/ // 业务实现类子包
│ │ │ ├── UserServiceImpl.java // 用户业务实现
│ │ │ └── OrderServiceImpl.java // 订单业务实现
│ │ └── resources/
│ │ ├── application.yml // 开发环境核心配置(修复扫描冲突)
│ │ └── application-prod.yml // 生产环境配置(屏蔽Swagger/JWT测试)
│ └── test/ // 测试包(默认生成,无需修改)
└── pom.xml // Maven依赖(完整,含SpringDoc/JWT)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 3. 完整Maven依赖配置
整合SpringDoc核心依赖、JWT认证依赖、Web核心依赖、Lombok,解决版本适配问题,直接替换项目原有pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.10</version>
<relativePath/>
</parent>
<groupId>cc.hep</groupId>
<artifactId>SpringBoot-Swagger</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot-Swagger</name>
<description>SpringBoot 3.5.10 + SpringDoc OpenAPI(分组+JWT)企业级实战</description>
<properties>
<java.version>17</java.version>
<springdoc.version>2.6.0</springdoc.version>
</properties>
<dependencies>
<!-- Spring Web核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringDoc OpenAPI(Swagger替代,适配SpringBoot3.x) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- Lombok(简化实体/业务代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JWT核心依赖:jjwt(SpringBoot3.x适配0.12.5) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<!-- 序列化依赖(非必需,防止令牌解析序列化问题) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
添加完成后,点击IDEA右侧「Maven」→「Reload All Maven Projects」刷新依赖,确保无红色报错。
# 4. 核心配置文件
resources/application.yml:
# 服务器配置
server:
port: 8080
servlet:
context-path: /api # 企业级接口统一前缀
# SpringDoc OpenAPI核心配置(核心:扫描包+UI配置,注释冲突的paths-to-match)
springdoc:
packages-to-scan: cc.hep.controller # 仅扫描控制器包,提升性能
# 注释掉这行!与分组路径冲突,导致扫描失效
# paths-to-match: /api/**
swagger-ui:
path: /swagger-ui/index.html # UI访问路径
operationsSorter: method # 接口按请求方式排序
tagsSorter: alpha # 分组标签按字母排序
persistAuthorization: true # 持久化JWT认证信息
api-docs:
enabled: true # 开启API文档JSON生成
path: /api-docs # 文档JSON数据路径
# 自定义OpenAPI文档元信息
openapi:
info:
title: 企业级微服务接口文档
description: 基于SpringBoot 3.5.10 + SpringDoc OpenAPI的多模块接口文档(用户+订单)
version: 2.0.0
contact:
name: 架构组
email: arch@hep.cc
url: https://www.hep.cc
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
# 多环境服务器配置
server:
dev:
url: http://localhost:8080/api
desc: 开发环境
test:
url: http://test.hep.cc/api
desc: 测试环境
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 5. SpringDoc核心配置类
整合接口分组(用户/订单)、JWT全局认证配置、文档元信息、服务器多环境,OpenApiConfig.java直接创建在cc.hep.config包下:
package cc.hep.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* SpringDoc OpenAPI 核心配置类
* 修复:移除路径冲突,增加兜底扫描配置,确保接口正常显示
*/
@Configuration
public class OpenApiConfig {
// ========== 从application.yml注入文档元信息 ==========
@Value("${openapi.info.title}")
private String title;
@Value("${openapi.info.description}")
private String description;
@Value("${openapi.info.version}")
private String version;
@Value("${openapi.info.contact.name}")
private String contactName;
@Value("${openapi.info.contact.email}")
private String contactEmail;
@Value("${openapi.info.contact.url}")
private String contactUrl;
@Value("${openapi.info.license.name}")
private String licenseName;
@Value("${openapi.info.license.url}")
private String licenseUrl;
// ========== 从application.yml注入多环境服务器信息 ==========
@Value("${openapi.server.dev.url}")
private String devServerUrl;
@Value("${openapi.server.dev.desc}")
private String devServerDesc;
@Value("${openapi.server.test.url}")
private String testServerUrl;
@Value("${openapi.server.test.desc}")
private String testServerDesc;
// ========== 全局OpenAPI配置(整合:元信息+JWT+多环境服务器) ==========
@Bean
public OpenAPI openApi() {
// 多环境服务器
Server devServer = new Server().url(devServerUrl).description(devServerDesc);
Server testServer = new Server().url(testServerUrl).description(testServerDesc);
// 文档元信息
Info info = new Info()
.title(title)
.description(description)
.version(version)
.contact(new Contact().name(contactName).email(contactEmail).url(contactUrl))
.license(new License().name(licenseName).url(licenseUrl));
// JWT安全方案配置
SecurityScheme jwtScheme = new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.name("Authorization")
.in(SecurityScheme.In.HEADER);
// 全局JWT认证
SecurityRequirement jwtSecurity = new SecurityRequirement().addList("JWT_AUTH");
// 构建OpenAPI
return new OpenAPI()
.info(info)
.servers(List.of(devServer, testServer))
.addSecurityItem(jwtSecurity)
.components(new io.swagger.v3.oas.models.Components()
.addSecuritySchemes("JWT_AUTH", jwtScheme));
}
// ========== 接口分组1:用户模块(兜底配置:明确扫描控制器+排除无用路径) ==========
@Bean
public GroupedOpenApi userModuleApi() {
return GroupedOpenApi.builder()
.group("用户模块")
.pathsToMatch("/user/**") // 匹配/user下所有接口(相对于context-path:/api)
.pathsToExclude("/none/**") // 排除无意义路径,避免扫描干扰
.packagesToScan("cc.hep.controller") // 扫描控制器包
.build();
}
// ========== 接口分组2:订单模块(同用户模块兜底配置) ==========
@Bean
public GroupedOpenApi orderModuleApi() {
return GroupedOpenApi.builder()
.group("订单模块")
.pathsToMatch("/order/**") // 匹配/order下所有接口
.pathsToExclude("/none/**")
.packagesToScan("cc.hep.controller")
.build();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# 6. 企业级多模块业务开发(用户+订单)
按实体类→业务层→控制器的分层规范开发,整合SpringDoc注解(接口文档描述)、敏感字段隐藏、参数校验,所有代码可直接复制。
# 6.1 实体类开发
# 6.1.1 用户实体(User.java)
包含敏感字段隐藏(密码)、Swagger字段描述,创建在cc.hep.model包下:
package cc.hep.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 用户实体类(企业级规范:Serializable+Lombok+Schema注解)
*/
@Data
@Schema(description = "用户实体类")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "用户ID", example = "1001", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "用户名", example = "zhangsan", requiredMode = Schema.RequiredMode.REQUIRED)
private String username;
@Schema(description = "用户密码(加密存储)", example = "e10adc3949ba59abbe56e057f20f883e", hidden = true)
private String password; // hidden=true:文档中隐藏敏感字段
@Schema(description = "用户邮箱", example = "zhangsan@hep.cc")
private String email;
@Schema(description = "创建时间", example = "2026-02-04 10:00:00")
private LocalDateTime createTime;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 6.1.2 订单实体(Order.java)
与用户实体规范一致,创建在cc.hep.model包下:
package cc.hep.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单实体类(新增,订单模块)
*/
@Data
@Schema(description = "订单实体类")
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "订单ID", example = "20260204001", requiredMode = Schema.RequiredMode.REQUIRED)
private String orderId;
@Schema(description = "用户ID", example = "1001", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
@Schema(description = "订单金额", example = "99.99", requiredMode = Schema.RequiredMode.REQUIRED)
private BigDecimal amount;
@Schema(description = "订单状态(0-待支付 1-已支付 2-已取消)", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer status;
@Schema(description = "订单创建时间", example = "2026-02-04 14:00:00")
private LocalDateTime createTime;
@Schema(description = "支付时间", example = "2026-02-04 14:05:00", nullable = true)
private LocalDateTime payTime;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 6.2 业务层开发(Service)
模拟数据库操作(企业级开发中替换为MyBatis/MyBatis-Plus),实现基础的增删改查,按接口+实现类规范开发。
# 6.2.1 用户业务层
(1)用户业务接口(UserService.java)
创建在cc.hep.service包下:
package cc.hep.service;
import cc.hep.model.User;
import java.util.List;
/**
* 用户业务层接口(企业级接口隔离规范)
*/
public interface UserService {
/**
* 根据ID查询用户
*/
User getUserById(Long id);
/**
* 查询所有用户
*/
List<User> listAllUsers();
/**
* 添加用户
*/
Boolean addUser(User user);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(2)用户业务实现类(UserServiceImpl.java)
创建在cc.hep.service.impl包下:
package cc.hep.service.impl;
import cc.hep.model.User;
import cc.hep.service.UserService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 用户业务实现类
* 模拟数据库操作,企业级开发中替换为MyBatis/MyBatis-Plus持久层调用
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Long id) {
User user = new User();
user.setId(id);
user.setUsername("zhangsan");
user.setEmail("zhangsan@hep.cc");
user.setCreateTime(LocalDateTime.now());
return user;
}
@Override
public List<User> listAllUsers() {
List<User> userList = new ArrayList<>();
// 模拟用户1
User user1 = new User();
user1.setId(1001L);
user1.setUsername("zhangsan");
user1.setEmail("zhangsan@hep.cc");
user1.setCreateTime(LocalDateTime.now());
// 模拟用户2
User user2 = new User();
user2.setId(1002L);
user2.setUsername("lisi");
user2.setEmail("lisi@hep.cc");
user2.setCreateTime(LocalDateTime.now());
userList.add(user1);
userList.add(user2);
return userList;
}
@Override
public Boolean addUser(User user) {
// 模拟添加逻辑,自动设置创建时间
user.setCreateTime(LocalDateTime.now());
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 6.2.2 订单业务层
(1)订单业务接口(OrderService.java)
创建在cc.hep.service包下:
package cc.hep.service;
import cc.hep.model.Order;
import java.util.List;
/**
* 订单业务层接口
*/
public interface OrderService {
/**
* 根据订单ID查询订单
*/
Order getOrderById(Long orderId);
/**
* 根据用户ID查询订单列表
*/
List<Order> listOrderByUserId(Long userId);
/**
* 创建订单
*/
Boolean createOrder(Order order);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(2)订单业务实现类(OrderServiceImpl.java)
创建在cc.hep.service.impl包下:
package cc.hep.service.impl;
import cc.hep.model.Order;
import cc.hep.service.OrderService;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 订单业务实现类
* 模拟数据库操作,企业级开发中替换为持久层调用
*/
@Service
public class OrderServiceImpl implements OrderService {
@Override
public Order getOrderById(Long orderId) {
Order order = new Order();
order.setOrderId(orderId);
order.setUserId(1001L);
order.setAmount(new BigDecimal("99.99"));
order.setStatus(1);
order.setCreateTime(LocalDateTime.now());
return order;
}
@Override
public List<Order> listOrderByUserId(Long userId) {
List<Order> orderList = new ArrayList<>();
// 模拟订单1
Order order1 = new Order();
order1.setOrderId(20260204001L);
order1.setUserId(userId);
order1.setAmount(new BigDecimal("99.99"));
order1.setStatus(1);
order1.setCreateTime(LocalDateTime.now());
// 模拟订单2
Order order2 = new Order();
order2.setOrderId(20260204002L);
order2.setUserId(userId);
order2.setAmount(new BigDecimal("199.99"));
order2.setStatus(2);
order2.setCreateTime(LocalDateTime.now());
orderList.add(order1);
orderList.add(order2);
return orderList;
}
@Override
public Boolean createOrder(Order order) {
// 模拟创建逻辑,自动设置创建时间
order.setCreateTime(LocalDateTime.now());
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 6.3 控制器开发(Controller)
整合SpringDoc注解(接口文档描述)、参数校验、响应状态码规范,用户控制器新增登录接口(生成JWT令牌),所有接口被JWT拦截器保护(登录接口除外)。
# 6.3.1 用户控制器(UserController.java)
创建在cc.hep.controller包下,包含登录接口(获取JWT令牌)+ 基础用户接口:
package cc.hep.controller;
import cc.hep.utils.JwtUtil;
import cc.hep.model.User;
import cc.hep.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理接口", description = "用户查询、新增、登录等核心接口(登录无需认证,其余需JWT)")
public class UserController {
@Autowired
private UserService userService;
// 注入JWT工具类
@Autowired
private JwtUtil jwtUtil;
// ========== 新增:登录接口(白名单,放行,生成JWT令牌) ==========
@PostMapping("/login")
@Operation(summary = "用户登录", description = "账号密码校验通过后,返回JWT令牌(模拟:账号zhangsan,密码123456)")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "登录成功,返回JWT令牌"),
@ApiResponse(responseCode = "400", description = "账号/密码为空"),
@ApiResponse(responseCode = "401", description = "账号或密码错误")
})
public ResponseEntity<Map<String, Object>> login(
@RequestParam @Parameter(description = "用户名", example = "zhangsan", required = true) String username,
@RequestParam @Parameter(description = "密码", example = "123456", required = true) String password
) {
Map<String, Object> result = new HashMap<>();
// 1. 参数校验
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
result.put("code", 400);
result.put("msg", "账号/密码不能为空");
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
// 2. 模拟账号密码校验(企业级开发中替换为MyBatis查询数据库)
if (!"zhangsan".equals(username) || !"123456".equals(password)) {
result.put("code", 401);
result.put("msg", "账号或密码错误");
return new ResponseEntity<>(result, HttpStatus.UNAUTHORIZED);
}
// 3. 校验通过:生成JWT令牌(用户ID固定为1001,模拟)
String token = jwtUtil.generateToken(1001L, username);
// 4. 返回结果(含令牌,前端需保存)
result.put("code", 200);
result.put("msg", "登录成功");
result.put("token", token); // 纯令牌(Swagger授权时需加Bearer前缀)
result.put("token_format", "Bearer " + token); // 完整格式(可直接复制到Swagger)
return new ResponseEntity<>(result, HttpStatus.OK);
}
// ========== 原有接口(无需修改,已被JWT拦截器保护) ==========
@GetMapping("/{id}")
@Operation(summary = "根据ID查询用户", description = "传入用户ID,返回用户信息(密码字段隐藏)")
@Parameters({@Parameter(name = "id", description = "用户ID", required = true, example = "1001")})
@ApiResponses({
@ApiResponse(responseCode = "200", description = "查询成功"),
@ApiResponse(responseCode = "400", description = "参数错误(ID为空/非正数)"),
@ApiResponse(responseCode = "404", description = "用户不存在")
})
public ResponseEntity<User> getUserById(@PathVariable Long id) {
if (id == null || id <= 0) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
User user = userService.getUserById(id);
return user == null ? new ResponseEntity<>(HttpStatus.NOT_FOUND) : new ResponseEntity<>(user, HttpStatus.OK);
}
@GetMapping("/list")
@Operation(summary = "查询所有用户", description = "返回系统所有用户列表(不含密码)")
@ApiResponse(responseCode = "200", description = "查询成功,返回用户列表")
public ResponseEntity<List<User>> listAllUsers() {
return new ResponseEntity<>(userService.listAllUsers(), HttpStatus.OK);
}
@PostMapping("/add")
@Operation(summary = "新增用户", description = "传入用户信息,完成用户新增(创建时间自动生成)")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "新增成功"),
@ApiResponse(responseCode = "400", description = "参数错误(用户名为空)")
})
public ResponseEntity<Boolean> addUser(@RequestBody User user) {
if (user == null || user.getUsername() == null || user.getUsername().isEmpty()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(userService.addUser(user), HttpStatus.CREATED);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 6.3.2 订单控制器(OrderController.java)
创建在cc.hep.controller包下,所有接口需JWT认证:
package cc.hep.controller;
import cc.hep.model.Order;
import cc.hep.service.OrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 订单模块控制器(新增,企业级规范)
*/
@RestController
@RequestMapping("/order")
@Tag(name = "订单管理接口", description = "订单查询、创建等核心接口(需JWT认证)")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{orderId}")
@Operation(summary = "根据订单ID查询订单", description = "传入订单ID,返回订单详细信息")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "查询成功"),
@ApiResponse(responseCode = "400", description = "订单ID为空"),
@ApiResponse(responseCode = "404", description = "订单不存在")
})
public ResponseEntity<Order> getOrderByOrderId(@PathVariable @Parameter(description = "订单ID", example = "20260204001", required = true) String orderId) {
if (orderId == null || orderId.isEmpty()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
Order order = orderService.getOrderByOrderId(orderId);
return order == null ? new ResponseEntity<>(HttpStatus.NOT_FOUND) : new ResponseEntity<>(order, HttpStatus.OK);
}
@GetMapping("/user/{userId}")
@Operation(summary = "根据用户ID查询订单列表", description = "传入用户ID,返回该用户所有订单")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "查询成功"),
@ApiResponse(responseCode = "400", description = "用户ID为空/非正数")
})
public ResponseEntity<List<Order>> listOrderByUserId(@PathVariable @Parameter(description = "用户ID", example = "1001", required = true) Long userId) {
if (userId == null || userId <= 0) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
return new ResponseEntity<>(orderService.listOrderByUserId(userId), HttpStatus.OK);
}
@PostMapping("/create")
@Operation(summary = "创建订单", description = "传入订单信息,完成订单创建(状态默认待支付)")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "创建成功"),
@ApiResponse(responseCode = "400", description = "参数错误(订单ID/用户ID为空)")
})
public ResponseEntity<Boolean> createOrder(@RequestBody Order order) {
if (order == null || order.getOrderId() == null || order.getUserId() == null) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(orderService.createOrder(order), HttpStatus.CREATED);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 7. 完整JWT认证体系集成
实现JWT工具类(生成/解析/校验)、JWT拦截器(全局接口认证)、Web配置(注册拦截器+配置白名单),确保只有携带有效令牌的请求才能访问业务接口,登录接口/Swagger路径放行。
# 7.1 JWT核心工具类(JwtUtil.java)
封装令牌生成、解析、校验、从请求头提取令牌的方法,256位密钥适配JJWT 0.12.5,创建在cc.hep.common.utils包下:
package cc.hep.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类(企业级标准封装,SpringBoot3.x+JJWT0.12.5)
* 功能:生成令牌、解析令牌中的用户信息、校验令牌、从请求头提取令牌
*/
@Component
public class JwtUtil {
// 1. 256位密钥(JJWT0.12+要求至少256位,可自行生成,生产环境放配置文件/配置中心)
private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor("12345678901234567890123456789012".getBytes());
// 2. 令牌过期时间:2小时(单位:毫秒,生产环境可配置)
private static final long EXPIRE_TIME = 2 * 60 * 60 * 1000L;
// 3. 请求头中Token的键名
public static final String TOKEN_HEADER = "Authorization";
// 4. Token前缀(与Swagger配置一致)
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 生成JWT令牌(传入用户ID、用户名,自定义载荷)
*/
public String generateToken(Long userId, String username) {
// 自定义载荷(存储用户关键信息,不存敏感信息如密码)
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("username", username);
// 生成令牌
return Jwts.builder()
.claims(claims) // 载荷
.subject(username) // 主题(可选)
.issuedAt(new Date()) // 签发时间
.expiration(new Date(System.currentTimeMillis() + EXPIRE_TIME)) // 过期时间
.signWith(SECRET_KEY, SignatureAlgorithm.HS256) // 签名算法+密钥
.compact(); // 生成令牌字符串
}
/**
* 从令牌中解析出载荷(获取用户信息)
*/
public Claims getClaimsFromToken(String token) {
try {
return Jwts.parser()
.verifyWith(SECRET_KEY) // 验证密钥
.build()
.parseSignedClaims(token)
.getPayload();
} catch (Exception e) {
return null; // 令牌无效/过期/签名错误,返回null
}
}
/**
* 校验令牌是否有效(未过期+签名正确)
*/
public boolean isTokenValid(String token) {
Claims claims = getClaimsFromToken(token);
if (claims == null) {
return false;
}
// 检查是否过期
return !claims.getExpiration().before(new Date());
}
/**
* 从Http请求头中提取令牌(自动去掉Bearer前缀)
*/
public String extractTokenFromRequest(HttpServletRequest request) {
String header = request.getHeader(TOKEN_HEADER);
if (StringUtils.hasText(header) && header.startsWith(TOKEN_PREFIX)) {
return header.substring(TOKEN_PREFIX.length()); // 去掉前缀,返回纯令牌
}
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 7.2 JWT全局拦截器(JwtInterceptor.java)
接口执行前校验令牌,无令牌/令牌无效返回401未授权,令牌有效则解析用户信息放入请求,创建在cc.hep.config.interceptor包下:
package cc.hep.config.interceptor;
import cc.hep.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* JWT拦截器(全局校验令牌,企业级标准)
* 逻辑:放行白名单 → 提取Token → 校验Token → 解析用户信息(可选)→ 放行/拦截
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
/**
* 前置拦截(接口执行前校验Token)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 提取令牌(从请求头)
String token = jwtUtil.extractTokenFromRequest(request);
// 2. 校验令牌:无令牌/令牌无效 → 拦截(返回401未授权)
if (token == null || !jwtUtil.isTokenValid(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401状态码
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"未授权,请先登录获取令牌\"}");
return false; // 拦截请求
}
// 3. 令牌有效:解析用户信息(可选,放入request供后续接口使用)
Claims claims = jwtUtil.getClaimsFromToken(token);
request.setAttribute("userId", claims.get("userId"));
request.setAttribute("username", claims.get("username"));
// 4. 放行请求
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 7.3 Web配置类(WebMvcConfig.java)
注册JWT拦截器+配置白名单,放行Swagger所有相关路径、用户登录接口,避免Swagger无法访问/登录接口被拦截,创建在cc.hep.config包下:
package cc.hep.config;
import cc.hep.config.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* WebMvc配置类:注册JWT拦截器+配置白名单(放行无需认证的路径)
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
/**
* 注册拦截器,配置拦截规则+白名单
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/api/**") // 拦截所有/api前缀的接口(企业级统一前缀)
.excludePathPatterns( // 白名单:放行以下路径,无需认证
// 1. Swagger所有相关路径(必须全部放行,否则Swagger页面打不开/授权失败)
"/api/swagger-ui/**",
"/api/api-docs/**",
"/api/v3/api-docs/**",
"/api/swagger-resources/**",
"/api/webjars/**",
// 2. 登录接口(核心,放行后才能获取令牌)
"/api/user/login"
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 8. 生产环境屏蔽配置
企业级开发中,生产环境必须屏蔽Swagger文档(避免接口信息泄露)、禁用JWT测试相关功能,创建resources/application-prod.yml:
# 生产环境核心配置:屏蔽Swagger所有功能
springdoc:
api-docs:
enabled: false # 禁用API文档JSON生成
swagger-ui:
enabled: false # 禁用Swagger UI页面
# 服务器配置与开发环境保持一致
server:
port: 8080
servlet:
context-path: /api
2
3
4
5
6
7
8
9
10
11
12
生产环境启动方式(指定环境配置):
# 方式1:jar包启动
java -jar SpringBoot-Swagger-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
# 方式2:IDEA中启动
Run/Debug Configurations → VM options → 添加:-Dspring.profiles.active=prod
2
3
4
5
# 9. Swagger UI接口文档访问与测试流程
# 9.1 项目启动
运行cc.hep.SpringBootSwaggerApplication启动类,控制台无红色报错即启动成功,关键日志:
- Tomcat started on port(s): 8080 (http)
- Started SpringBootSwaggerApplication in X seconds
# 9.2 Swagger UI访问地址
支持以下两个地址(均能正常打开,SpringDoc做了路径映射):
- 基础地址:
http://localhost:8080/api/swagger-ui/index.html - 实际测试地址:
http://localhost:8080/api/swagger-ui/swagger-ui/index.html
# 9.3 完整接口测试流程(以用户模块为例)
步骤1:登录接口获取JWT令牌
- 打开Swagger UI页面,左侧展开「用户管理接口」,找到
POST /user/login接口; - 点击接口右侧Try it out按钮(橙色),出现
username和password输入框; - 分别输入:
username=zhangsan、password=123456; - 点击下方Execute按钮(蓝色),向下滚动查看Response body;
- 复制
token_format对应的值(已包含Bearer前缀),示例:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEwMDEsInVzZXJuYW1lIjoiemhhbmdzYW4iLCJzdWIiOiJ6aGFuZ3NhbiIsImlhdCI6MTcxNzUyNjQzNSwiZXhwIjoxNzE3NTMzNjM1fQ.2f9ZpRbQ7eU8X7s9D6F5G4H3J2K1L0M9N8O7P6Q5R4S3T21
步骤2:Swagger全局JWT授权
- 点击Swagger页面右上角绿色Authorize按钮,弹出授权弹窗;
- 在弹窗输入框中直接粘贴步骤1复制的token_format值(无需修改);
- 点击弹窗底部Authorize按钮,弹窗顶部显示
Authorized绿色提示; - 点击Close关闭弹窗,右上角按钮变为红色Unauthorize(代表授权成功)。
步骤3:测试需要认证的接口(GET /user/{id})
- 左侧找到
GET /user/{id}接口,点击Try it out按钮; - 在
id输入框中输入1001(合法参数); - 点击Execute按钮,向下滚动查看测试结果:
- Request Headers:包含
Authorization: Bearer 你的令牌(授权生效); - Response Code:200(请求成功);
- Response body:返回用户信息,无password字段(敏感字段隐藏成功)。
- Request Headers:包含
步骤4:异常场景测试(未授权访问)
- 点击右上角Unauthorize按钮,取消JWT授权;
- 再次调用
GET /user/1001接口,点击Execute; - 查看Response Code:401,Response body:
{"code":401,"msg":"未授权,请先登录获取有效JWT令牌"}(拦截器生效)。
# 9.4 订单模块测试流程
与用户模块一致,先完成JWT授权,再调用订单接口(如GET /order/20260204001),验证接口正常返回数据即可。

# 10. 常见问题与解决方案
Swagger UI显示「No operations defined in spec!」
核心原因:全局路径匹配paths-to-match: /api/**与分组路径/user/**冲突;
解决方案:注释application.yml中的paths-to-match: /api/**,重启项目+浏览器强制刷新(F5)。
Swagger Authorize按钮点击无弹窗/授权后接口仍返回401
- 检查
OpenApiConfig中JWT安全方案的key(JWT_AUTH)是否与SecurityRequirement中的key一致; - 确认粘贴的令牌包含完整的
Bearer前缀(注意英文空格,不可省略); - 检查令牌是否过期(JWT工具类配置2小时,过期后重新登录获取);
- 检查
JwtUtil中的256位密钥是否正确,生成/校验令牌使用同一密钥。
Swagger页面访问404
- 检查
server.servlet.context-path是否配置为/api,访问地址必须携带该前缀; - 检查
WebMvcConfig的白名单是否包含所有Swagger相关路径(/api/swagger-ui/**等); - 确认SpringDoc依赖已正确引入,Maven刷新后无报错。
JWT拦截器拦截登录接口/Swagger路径
解决方案:检查WebMvcConfig的excludePathPatterns,确保包含:
- 所有Swagger相关路径(
/api/swagger-ui/**、/api/api-docs/**等); - 登录接口路径(
/api/user/login)。
启动项目时报错「Failed to create SecretKey for HS256」
核心原因:JJWT 0.12+要求密钥≥256位;
解决方案:修改JwtUtil中的密钥为32位字符串(如12345678901234567890123456789012),确保密钥字节数为32(256位)。
# 11. 核心总结
- SpringBoot3.x适配:放弃传统Swagger2,使用
springdoc-openapi-starter-webmvc-ui核心依赖,注解使用io.swagger.v3.oas.annotations包下的; - 扫描冲突修复:注释
application.yml中的全局路径匹配paths-to-match: /api/**,由分组路径/user/**//order/**接管扫描; - JWT集成核心:实现「工具类+拦截器+Web配置」,白名单放行Swagger路径和登录接口,其余接口统一认证;
- 企业级规范:敏感字段用
@Schema(hidden=true)隐藏,生产环境通过多环境配置屏蔽Swagger,接口按「实体→业务→控制器」分层开发; - Swagger测试规范:先通过登录接口获取有效令牌,再完成全局授权,最后测试业务接口,确保请求头携带
Authorization令牌。
本教程所有代码可直接复制运行,适配SpringBoot 3.5.10最新版本,整合了企业开发中必备的接口文档、JWT认证、多模块开发、生产环境管控等功能,可直接作为实际项目的基础模板。