knife4j
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案 。
- 官网
- 访问地址都是/doc.html
前言
最近打算给自己的后台管理系统增加一个API管理,但是原生的Swagger,十分嫌弃,太丑了,所以找了个Knife4j,感觉上好看很多了。
Maven版本
这里是基于springboot-2.3.5版本,OpenApi2。特别说明一下,我这里使用的是单体应用,如果架构不同会有区分微服务版本、网关版本等。
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.9</version>
</dependency>
Swagger2Config
因为代码篇幅较多,我将对应的说明描述下代码里面,就不在外说明了。
@Configuration
@EnableSwagger2WebMvc
public class Swagger2Config {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()).enable(true)
.select()
/***
重要的两个方法:
apis():指定要生成文档的接口包基本路径
paths():指定针对哪些请求生成接口文档
参考官方资料:http://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
****/
// 1. 将 top.zsmile 设置为基础包扫描。
// 2. 只包含 RestController 注解,也可以使用 Api 注解,但是对应的类需要添加。
// 3. 针对所有路径扫描
.apis(RequestHandlerSelectors.basePackage("top.zsmile"))
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
.build()
.securitySchemes(securityScheme())
.securityContexts(securityContexts());
}
/**
* 安全计划
* 使用 X-ACCESS-TOKEN 作为请求头,添加在 HTTP Header 里面
*
* @return
*/
@Bean
public List<ApiKey> securityScheme() {
return Collections.singletonList(new ApiKey(CommonConstant.X_ACCESS_TOKEN, CommonConstant.X_ACCESS_TOKEN, "header"));
}
/**
* 新增 securityContexts 保持登录状态
*/
private List<SecurityContext> securityContexts() {
return Collections.singletonList(SecurityContext.builder()
.securityReferences(defaultAuth())
//设置需要登录认证的路径,非noauth开头的都需要Token认证
.forPaths(PathSelectors.regex("^(?!noauth).*$"))
.build());
}
/**
* 默认授权
*
* @return
*/
private List<SecurityReference> defaultAuth() {
// 权限范围:Scope=>全局,设置在keyName=>X-ACCESS-TOKEN
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Collections.singletonList(new SecurityReference(CommonConstant.X_ACCESS_TOKEN, authorizationScopes));
}
/**
* 文档信息
*
* @return
*/
private ApiInfo apiInfo() {
Contact smileX = new Contact("SmileX", "https://github.com/smileluck", "");
return new ApiInfoBuilder()
.title("SmileX项目接口文档")
.description("API接口文档")
.version("1.0.0")
.termsOfServiceUrl("https://github.com/smileluck")
.contact(smileX)
.build();
}
}
经过这个配置,其实已经可以使用了。
增强
接下来是为了打开knife4j的增强,可以采用注解 @EnableKnife4j 开启增强。
@EnableKnife4j
@Configuration
@EnableSwagger2WebMvc
public class Swagger2Config {}
或者通过yaml的形式进行配置。
knife4j:
# 开启增强
enable: true
# 开启生产环境保护
production: false
# 开启Swagger的Basic认证功能,默认是false
basic:
enable: false
# Basic认证用户名
username: knifeAdmin
# Basic认证密码
password: knifeAdmin
更多配置请查看官方文档。
注意事项
Shiro整合使用
需要给knife4j 的路径进行放行,否则当我们访问时会出现401的问题。
/*swagger*/
chainDefinition.addPathDefinition("/doc.html", "anon");
chainDefinition.addPathDefinition("/webjars/**", "anon");
chainDefinition.addPathDefinition("/v2/api-docs", "anon");
chainDefinition.addPathDefinition("/swagger-resources", "anon");
如果使用了权限框架,如shiro、SpringSecurity,需要WebMvcConfigurer里面添加配置:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 显示swagger-ui.html文档展示页,还必须注入swagger资源:
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
统一返回实体
@ApiModel("统一返回结果")
public class R<T> implements Serializable {
public static final long serialVersionUID = 1L;
@ApiModelProperty("状态码")
private int code;
@ApiModelProperty("响应信息")
private String msg;
@ApiModelProperty("结果")
private T data;
@ApiModelProperty("是否成功")
private boolean success;
private R(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
this.success = ResultCode.SUCCESS.getCode() == code;
}
private R(int code, String msg) {
this(code, msg, null);
}
private R(ResultCode resultCode) {
this(resultCode.getCode(), resultCode.getMessage());
}
private R(ResultCode resultCode, String msg) {
this(resultCode.getCode(), msg);
}
private R(ResultCode resultCode, T data) {
this(resultCode.getCode(), resultCode.getMessage(), data);
}
private R(ResultCode resultCode, String msg, T data) {
this(resultCode.getCode(), msg, data);
}
public static <T> R<T> success() {
return new R(ResultCode.SUCCESS);
}
public static <T> R<T> success(ResultCode resultCode) {
return new R(resultCode);
}
public static <T> R<T> success(ResultCode resultCode, String msg) {
return new R(resultCode, msg);
}
public static <T> R<T> success(String msg) {
return new R(ResultCode.SUCCESS, msg);
}
public static <T> R<T> success(T data) {
return new R(ResultCode.SUCCESS, data);
}
public static <T> R<T> success(String msg, T data) {
return new R(ResultCode.SUCCESS, msg, data);
}
public static <T> R<T> fail() {
return new R(ResultCode.FAILURE);
}
public static <T> R<T> fail(Integer code, String msg) {
return new R(code, msg);
}
public static <T> R<T> fail(ResultCode resultCode) {
return new R(resultCode);
}
public static <T> R<T> fail(ResultCode resultCode, String msg) {
return new R(resultCode, msg);
}
public static <T> R<T> fail(String msg) {
return new R(ResultCode.FAILURE, msg);
}
public static <T> R<T> fail(T data) {
return new R(ResultCode.FAILURE, data);
}
public static <T> R<T> fail(String msg, T data) {
return new R(ResultCode.FAILURE, msg, data);
}
public static boolean isSuccess(@Nullable R r) {
return ObjectUtils.nullSafeEquals(ResultCode.SUCCESS, r.getCode());
}
public static boolean isNotSuccess(@Nullable R r) {
return !isSuccess(r);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public boolean getSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
界面
设置全局token
只要在这里填入token,那么就会自动注入到所有请求的请求头中使用。