Appearance
- 3.0.0 版本兼容 Spring Boot 2.2.x~2.5.x,不完全支持 Spring Boot 2.6 及以上版本,且社区维护已基本停止。
- 如果你用的 Spring Boot 2.6 或更高版本,建议考虑换 springdoc-openapi。
简介
Swagger是一个规范和完整的API框架,可用于生成、描述、调用Restful风格的Web服务的接口文档。
如果你在SpringBoot中使用的话,在项目启动后可以自动生成在线可调用的API文档,非常方便!
在SpringBoot中集成
使用官方Starter来整合Swagger是非常简单的,我们将采用此种方式。
- 首先在pom.xml中添加 springfox 官方Swagger依赖;
xml
<!--springfox swagger官方Starter-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox-swagger.version}</version>
</dependency>- 由于Spring默认的路径匹配策略为PATH_PATTERN_PARSER,而SpringFox假设为ANT_PATH_MATCHER,所以需要修改application.yml配置文件,添加如下配置;
yaml
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER- 添加Swagger的Java配置,配置好Api信息和需要生成接口文档的类扫描路径,注意在SpringBoot2.6.x以上版本整合时由于兼容性问题需要配置一个BeanPostProcessor的Bean;
java
/**
* @auther macrozheng
* @description Swagger相关配置
* @date 2022/11/23
* @github https://github.com/macrozheng
*/
@Configuration
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SwaggerUI演示")
.description("mall-tiny")
.contact(new Contact("macro", null, null))
.version("1.0")
.build();
}
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
}- 访问API文档信息,访问地址:http://localhost:8088/swagger-ui/

开始使用
相关注解
由于Swagger主要是通过注解来标注文档内容的,这里我们先来学习下它的常用注解。
| 注解名称 | 描述 | 常用属性 |
|---|---|---|
@Api | 用于类,标识这个类是Swagger的资源 | tags:给该类下的接口设置标签 |
@Tag | 可用于类或方法,声明一个标签 | name:标签名称description:标签描述 |
@ApiIgnore | 忽略该类的文档生成 | value:添加备注 |
@ApiOperation | 用于方法,用于描述一个HTTP请求方法 | value:给方法添加描述 |
@ApiParam | 用于参数,用于描述请求参数 | value:参数描述name:参数名称defaultValue:参数默认值required:参数是否必填allowableValues:参数允许范围type:参数类型 |
@ApiImplicitParam | 代表一个单个API操作,与@ApiImplicitParams联用 | paramType:参数请求类型dataTypeClass:参数值类型其他类型同 @ApiParam |
@ApiImplicitParams | 多个@ApiImplicitParam注解的集合 | 参数为@ApiImplicitParam数组 |
@ApiModel | 用于类,声明一个Swagger的模型 | value:模型名称description:模型描述 |
@ApiProperty | 用于参数,声明Swagger模型的属性或填充数据 | value:属性描述name:属性名称allowableValues:允许值 |
@ApiResponse | 用于描述一个可能的返回结果 | responseCode:返回状态码message:返回信息 |
@ApiResponses | @ApiResponse的集合 | 参数为@ApiResponse数组 |
相关配置
下面是Swagger的一些常用配置,在application.yml文件中配置即可。
yaml
springfox:
documentation:
# 是否启用Swagger扫描代码生成文档
enabled: true
open-api:
# 是否启用Swagger的open-api
enabled: false
swagger-ui:
# 是否启用Swagger的Web UI
enabled: true
# 配置文档基础路径,此时路径为:/doc/swagger-ui/index.html
base-url: /doc基本使用
- 使用时我们只需把Swagger对应的注解添加到接口方法上即可;
java
/**
* @auther macrozheng
* @description 品牌管理Controller
* @date 2019/4/19
* @github https://github.com/macrozheng
*/
@Controller
@Api(tags = "PmsBrandController")
@Tag(name = "PmsBrandController", description = "商品品牌管理")
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService brandService;
private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
@ApiOperation("分页查询品牌列表")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
@PreAuthorize("hasRole('ADMIN')")
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("页码") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "5")
@ApiParam("每页数量") Integer pageSize) {
List<PmsBrand> brandList = brandService.listBrand(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(brandList));
}
}- 如果不想在参数上绑定注解,想手动定义的话可以使用
@ApiImplicitParam注解;
java
/**
* @auther macrozheng
* @description 后台用户管理
* @date 2018/4/26
* @github https://github.com/macrozheng
*/
@Controller
@Api(tags = "UmsAdminController")
@Tag(name = "UmsAdminController", description = "后台用户管理")
@RequestMapping("/admin")
public class UmsAdminController {
@Autowired
private UmsAdminService adminService;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ApiOperation(value = "登录以后返回token")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "密码", required = true, dataTypeClass = String.class)
})
@ApiResponses({
@ApiResponse(responseCode = "401", description = "暂未登录或token已经过期"),
@ApiResponse(responseCode = "403", description = "没有相关权限"),
})
@ResponseBody
public CommonResult login(@RequestParam String username, @RequestParam String password) {
String token = adminService.login(username, password);
if (token == null) {
return CommonResult.validateFailed("用户名或密码错误");
}
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("tokenHead", tokenHead);
return CommonResult.success(tokenMap);
}
}- 实体类上的注解,可以采用如下方式定义。
java
@ApiModel(value = "PmsBrand",description = "商品品牌")
public class PmsBrand implements Serializable {
@ApiModelProperty(value = "主键ID")
private Long id;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "首字母")
private String firstLetter;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "是否为品牌制造商:0->不是;1->是")
private Integer factoryStatus;
@ApiModelProperty(value = "是否显示")
private Integer showStatus;
@ApiModelProperty(value = "产品数量")
private Integer productCount;
@ApiModelProperty(value = "产品评论数量")
private Integer productCommentCount;
@ApiModelProperty(value = "品牌logo")
private String logo;
@ApiModelProperty(value = "专区大图")
private String bigPic;
@ApiModelProperty(value = "品牌故事")
private String brandStory;
}结合Spring Security使用
我们经常会在项目中使用Spring Security实现登录认证,接下来我们来讲下如何使用Swagger整合Spring Security,实现访问需要登录认证的接口。
- 如何访问需要登录认证的接口?只需在访问接口时添加一个合法的Authorization请求头即可,下面是Swagger相关配置;
java
/**
* @auther macrozheng
* @description Swagger相关配置(带认证)
* @date 2022/11/23
* @github https://github.com/macrozheng
*/
@Configuration
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller"))
.paths(PathSelectors.any())
.build()
//添加登录认证
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
private List<SecurityScheme> securitySchemes() {
//设置请求头信息
List<SecurityScheme> result = new ArrayList<>();
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
result.add(apiKey);
return result;
}
private List<SecurityContext> securityContexts() {
//设置需要登录认证的路径
List<SecurityContext> result = new ArrayList<>();
result.add(getContextByPath("/brand/.*"));
return result;
}
private SecurityContext getContextByPath(String pathRegex) {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(pathRegex))
.build();
}
private List<SecurityReference> defaultAuth() {
List<SecurityReference> result = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
result.add(new SecurityReference("Authorization", authorizationScopes));
return result;
}
}- 我们需要在Spring Security中配置好Swagger静态资源的无授权访问,比如首页访问路径
/swagger-ui/;
java
/**
* @auther macrozheng
* @description SpringSecurity的配置
* @date 2018/4/26
* @github https://github.com/macrozheng
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Lazy
@Autowired
private UmsAdminService adminService;
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
.disable()
.sessionManagement()// 基于token,所以不需要session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问
"/",
"/swagger-ui/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/swagger-resources/**",
"/v2/api-docs/**"
)
.permitAll()
.antMatchers("/admin/login")// 对登录注册要允许匿名访问
.permitAll()
.antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
.permitAll()
.anyRequest()// 除上面外的所有请求全部需要鉴权认证
.authenticated();
// 禁用缓存
httpSecurity.headers().cacheControl();
// 添加JWT filter
httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//添加自定义未授权和未登录结果返回
httpSecurity.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint);
return httpSecurity.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
//获取登录用户信息
return username -> {
AdminUserDetails admin = adminService.getAdminByUsername(username);
if (admin != null) {
return admin;
}
throw new UsernameNotFoundException("用户名或密码错误");
};
}
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter();
}
}- 调用登录接口获取token,账号密码为
admin:123456;

- 点击Authorize按钮后输入Authorization请求头,之后就可以访问需要登录认证的接口了。
