knife4j是什么?可以关注一下官网,简单说就是对基于swagger的增强版本。【官网】 本文基于以下基础进行集成:
- springboot 2.7.0(2.6.7 也可,推荐使用GA版本)
- knife4j3.0.3
- knife4j-aggregation 2.0.9
- spring cloud 2021.0.1
- 本文基于eureka集成(后续会介绍基于gateway的集成方式)
一、工程结构
使用到的工程说明:
- eboot-center:eureka注册中心(服务端)
- eboot-knife4j:文档服务
- eboot-common:包含了一些基础的认证、全局异常等处理,本文暂不需要
- eboot-gateway:spring-cloud-gateway,大多是情况下微服务均走网关,下一步会实现基于gateway的knife集成
- eboot-modulars:即各个业务子系统,这里分了三个:认证管理、文件管理、系统管理
- eboo-ui:前端项目,本文不需要
二、eboot-center服务端
普通的eureka服务端配置
主要配置如下:
代码语言:javascript复制<!--表示为web工程-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
代码语言:javascript复制server:
port: 8761
eureka:
instance:
hostname: localhost
appname: eboot-provider
leaseRenewalIntervalInSeconds: 1
lease-expiration-duration-in-seconds: 2
prefer-ip-address: true
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
boot:
admin:
client:
url: http://localhost:8888
application:
name: boot-provider
三、eboot-knife4j服务配置
文档服务的主要配置均在此,其实它只是一个web工程,详细配置参数见【eureka参数配置】
主要配置如下:
代码语言:javascript复制 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-aggregation-spring-boot-starter</artifactId>
</dependency>
请注意以下的serviceName和location:
1. serviceName:eureka是根据这个进行接口统计请求的,因此这个一定一定要正确,和对应服务中的对应起来。
2. location:业务系统起来之后可以访问下这个地址试试是否可以正常返回json数据,注意group,未配置的话是default,如果配置了使用业务系统配置的即可(例如:boot-auth的分组我使用了auth)
代码语言:javascript复制server:
port: 9999
knife4j:
enableAggregation: true
setting:
footerCustomContent: Copyright 2022-2023 【小尘哥】
eureka:
enable: true
serviceUrl: http://127.0.0.1:8761/eureka/
routeAuth:
enable: true
username: admin
password: 123456
routes:
- name: 认证体系
serviceName: boot-auth
location: http://localhost:9091/auth/v2/api-docs?group=auth
servicePath: /auth
- name: 文件服务
serviceName: boot-file
location: http://localhost:9092/file/v2/api-docs?group=default
servicePath: /file
- name: 系统服务
serviceName: boot-system
location: http://localhost:9093/system/v2/api-docs?group=default
servicePath: /system
放行静态资源和解决跨域问题
注意:跨域问题在每个服务中均需解决,否则接口请求报错
代码语言:javascript复制 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //配置拦截器访问静态资源 registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); }
四、业务系统系统
基本和普通的集成swagger一样,以下以eboot-auth为例,只列出关键配置
- 启用eureka客户端
- 集成knife
- 加入guava,否则swagger可能会有各种奇葩问题
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-micro-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency>
- knife4j.enable:启用knife增强功能,否则就是普通的swagger
- spring.mvc.pathmatch.matching-strategy,springboot 2.6.0开始MVC 处理映射匹配的默认策略已从AntPathMatcher更改为PathPatternParser,但是knife依然是AntPathMatcher模式,因此需手动修改回去,否则会出现“Failed to start bean ‘ documentationPluginsBootstrapper ‘”
- 其他的eureka正常配置即可
server:
port: 9091
servlet:
context-path: /auth
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
instance:
appname: boot-auth
leaseRenewalIntervalInSeconds: 1
lease-expiration-duration-in-seconds: 2
prefer-ip-address: true
instance-id: 127.0.0.1:9091
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
knife4j:
enable: true
swagger配置如下:
代码语言:javascript复制package com.mos.eboot.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
/**
* swagger配置
*
* @author 小尘哥
* @date 2022/05/19
*/
@Configuration
@EnableSwagger2WebMvc
public class SwaggerConfiguration {
@Bean
@Order(value = 1)
public Docket groupRestApi() {
Contact contact = new Contact("小尘哥","http://localhost:9091/auth/","grq100296@163.com");
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("统一认证服务 RESTful APIs")
.description("统一认证服务相关接口")
.termsOfServiceUrl("http://localhost:9091/auth/")
.contact(contact)
.version("1.0")
.build())
//分组名称
.groupName("auth")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.mos.eboot.auth.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
代码语言:javascript复制接口示例,请注意@ApiSupport即为knife增强注解,可以为当前类或者接口增加作者、序号等。包括@DynamicParameter、@DynamicResponseParameters、ApiOperationSupport、@Ignore等
package com.mos.eboot.auth.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.mos.eboot.auth.entity.SysUser;
import com.mos.eboot.auth.form.LoginBody;
import com.mos.eboot.auth.service.ISysUserService;
import com.mos.eboot.commom.core.auth.domain.LoginUser;
import com.mos.eboot.commom.core.result.ResultModel;
import com.mos.eboot.commom.core.result.ResultStatus;
import com.mos.eboot.commom.core.util.JwtUtils;
import com.mos.eboot.common.security.auth.AuthUtil;
import com.mos.eboot.common.security.service.TokenService;
import com.mos.eboot.common.security.util.SecurityUtils;
import com.xiaoleilu.hutool.util.ObjectUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* token 控制
*
* @author 小尘哥
*/
@Api(tags = "token认证")
@ApiSupport(author = "小尘哥")
@RestController
public class TokenController {
@Autowired
private TokenService tokenService;
@Autowired
private ISysUserService sysUserService;
@ApiImplicitParams({
@ApiImplicitParam(name = "username",value = "用户名",required = true),
@ApiImplicitParam(name = "password",value = "密码",required = true),
})
@ApiOperation(value = "登录")
@PostMapping("login")
public ResultModel<Map<String, Object>> login(LoginBody form) {
// 用户登录
LambdaQueryWrapper<SysUser> entityWrapper = Wrappers.lambdaQuery();
entityWrapper.eq(SysUser::getUsername, form.getUsername());
SysUser sysUser = sysUserService.getOne(entityWrapper);
LoginUser userInfo = new LoginUser();
userInfo.setUserid(sysUser.getId());
userInfo.setUsername(sysUser.getUsername());
// TODO: 完善LoginUser信息,记录登录日志
// 获取登录token
return ResultModel.defaultSuccessObj(tokenService.createToken(userInfo));
}
}