写在最前
如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
源码地址(后端):https://gitee.com/csps/mingyue
源码地址(前端):https://gitee.com/csps/mingyue-ui
文档地址:https://gitee.com/csps/mingyue/wikisapplication-common.yml
迁移配置
mingyue-auth => application-common.yml
将 Sa-Token 配置放入公共配置中,方便 components.security-schemes.apiKey.name= ${sa-token.token-name} 引用
# Sa-Token 配置
sa-token:# token名称 (同时也是 cookie 名称)token-name: Authorization# OAuth2.0 配置oauth2:is-code: trueis-implicit: trueis-password: trueis-client: true
优化 Swagger 配置
修改 SwaggerProperties
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.License;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;import java.util.Map;/*** SwaggerProperties** @author Strive* @date 2023/6/22 11:00*/
@Data
@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {/*** 文档基本信息*/@NestedConfigurationPropertyprivate InfoProperties info = new InfoProperties();/*** 组件*/@NestedConfigurationPropertyprivate Components components = null;/*** 是否开启 swagger*/private Boolean enabled = true;/*** 网关*/private String gateway;/*** 服务转发配置*/private Map<String, String> services;/*** 文档的基础属性信息** @see io.swagger.v3.oas.models.info.Info*/@Datapublic static class InfoProperties {/*** 标题*/private String title = null;/*** 描述*/private String description = null;/*** 联系人信息*/@NestedConfigurationPropertyprivate Contact contact = null;/*** 许可证*/@NestedConfigurationPropertyprivate License license = null;/*** 版本*/private String version = null;}
}
修改 SwaggerAutoConfiguration
删除 securityScheme() 方法,修改 springOpenAPI()
import com.csp.mingyue.doc.support.SwaggerProperties;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.servers.Server;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.SpringDocConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.context.annotation.Bean;import java.util.ArrayList;
import java.util.List;
import java.util.Set;/*** Swagger 配置** @author Strive*/
@RequiredArgsConstructor
@AutoConfiguration(before = SpringDocConfiguration.class)
@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnMissingClass("org.springframework.cloud.gateway.config.GatewayAutoConfiguration")
public class SwaggerAutoConfiguration {private final SwaggerProperties swaggerProperties;private final ServiceInstance serviceInstance;@Beanpublic OpenAPI springOpenAPI() {OpenAPI openApi = new OpenAPI();// 文档基本信息SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo();Info info = convertInfo(infoProperties);openApi.info(info);// 鉴权方式配置openApi.components(swaggerProperties.getComponents());Set<String> keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet();List<SecurityRequirement> list = new ArrayList<>();SecurityRequirement securityRequirement = new SecurityRequirement();keySet.forEach(securityRequirement::addList);list.add(securityRequirement);// servers 提供调用的接口地址前缀List<Server> serverList = new ArrayList<>();String path = swaggerProperties.getServices().get(serviceInstance.getServiceId());serverList.add(new Server().url(swaggerProperties.getGateway() + "/" + path));openApi.servers(serverList);return openApi;}/*** 装填文档的基础属性信息* @param infoProperties* @return io.swagger.v3.oas.models.info.Info*/private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) {Info info = new Info();info.setTitle(infoProperties.getTitle());info.setDescription(infoProperties.getDescription());info.setContact(infoProperties.getContact());info.setLicense(infoProperties.getLicense());info.setVersion(infoProperties.getVersion());return info;}}
优化 getSysUsers 接口
通过 getSysUsers 接口使用 Swagger 注解小小实战一下
接口类增加 @Tag(name = “用户管理模块”)
@Tag(name = "用户管理模块")
public class SysUserController {
接口增加 @Tag(name = “用户管理模块”)
@Operation(summary = "获取所有用户信息")
public R<List<SysUser>> getSysUsers() {return R.ok(sysUserService.list());
}
响应类增加 @Schema(description = “用户实体类”)
@Schema(description = "用户实体类")
public class SysUser implements Serializable {
响应类字段增加 @Schema(description = “用户ID”)
@Schema(description = "用户ID")
private Long userId;
接口文档增加身份校验
升级 mingyue-gateway,支持接口文档增加身份校验
接口文档一般在开发环境使用,极其不推荐在生产使用,将接口文档暴露出来非常不安全。开发环境公司内部使用时可以直接使用,无须增加身份校验,如果暴露出去,还是增加一个身份校验比较好,安全些。
增加 SpringDocConfiguration 配置
import lombok.Data;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.SwaggerUiConfigParameters;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** SpringDoc Config** @author Strive* @date 2023-6-22*/
@Configuration(proxyBeanMethods = false)
public class SpringDocConfiguration {/*** 当 swagger.enabled = true 向 Bean 容器中注册改对象* @return*/@Bean@Lazy(false)@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true", matchIfMissing = true)public List<GroupedOpenApi> apis(SwaggerUiConfigParameters swaggerUiConfigParameters,SwaggerDocProperties swaggerProperties) {List<GroupedOpenApi> groups = new ArrayList<>();// 读取配置服务,添加接口分组,以服务为纬度进行分组for (String value : swaggerProperties.getServices().values()) {swaggerUiConfigParameters.addGroup(value);}return groups;}@Data@Component@ConfigurationProperties("swagger")public class SwaggerDocProperties {/*** 添加接口文档的服务信息*/private Map<String, String> services;/*** 认证参数*/private SwaggerBasic basic = new SwaggerBasic();@Datapublic class SwaggerBasic {/*** 是否开启 basic 认证*/private Boolean enabled;/*** 用户名*/private String username;/*** 密码*/private String password;}}}
增加 SwaggerBasicGatewayFilter 过滤器
import com.csp.mingyue.gateway.config.SpringDocConfiguration;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;/*** Swagger 开启 Basic 认证** @author Strive* @date 2023/6/22*/
@Slf4j
@RequiredArgsConstructor
public class SwaggerBasicGatewayFilter implements GlobalFilter {private static final String API_URI = "/v3/api-docs";private static final String BASIC_PREFIX = "Basic ";private final SpringDocConfiguration.SwaggerDocProperties swaggerProperties;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();if (!request.getURI().getPath().contains(API_URI)) {return chain.filter(exchange);}if (hasAuth(exchange)) {return chain.filter(exchange);}else {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, "Basic Realm=\"mingyue\"");return response.setComplete();}}/*** 简单的basic认证* @param exchange 上下文* @return 是否有权限*/private boolean hasAuth(ServerWebExchange exchange) {ServerHttpRequest request = exchange.getRequest();String auth = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);log.info("Basic认证信息为:{}", auth);if (!StringUtils.hasText(auth) || !auth.startsWith(BASIC_PREFIX)) {return Boolean.FALSE;}String username = swaggerProperties.getBasic().getUsername();String password = swaggerProperties.getBasic().getPassword();String encodeToString = Base64Utils.encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));return auth.equals(BASIC_PREFIX + encodeToString);}}
增加 GatewayConfiguration 配置
import com.csp.mingyue.gateway.filter.SwaggerBasicGatewayFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 网关配置** @author Strive*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
public class GatewayConfiguration {@Bean@ConditionalOnProperty(name = "swagger.basic.enabled")public SwaggerBasicGatewayFilter swaggerBasicGatewayFilter(SpringDocConfiguration.SwaggerDocProperties swaggerProperties) {return new SwaggerBasicGatewayFilter(swaggerProperties);}}
修改 Nacos mingyue-gateway.yml 配置
通过
enabled
控制是否开启接口文档密码校验,通过username
与password
配置登录接口文档的用户名与密码
swagger:basic:# 是否开启接口文档密码校验enabled: trueusername: mingyuepassword: mingyue
启动测试
打开 swagger-ui: http://mingyue-gateway:9100/swagger-ui.html,会弹出登录框,输入 Nacos 中配置的用户名密码登录即可,查看是否配置成功!
小结
Swagger 接口文档基础功能已经可以使用,但仍有很多很多需要做的地方,比如:
- Authorize 功能,也就是 Token 还未使用;
- 基于
Openapi
结构体接入第三方工具,如:Apifox
、Postman
等。为什么有 Swagger-UI ,要接入第三方工具?其实 Swagger-UI 很不好用,哈哈哈~; - 导出接口文档,如PDF等文本格式;
- 。。。
山高路远,但仍要脚踏实地,收回来!下一篇我们先控制接口访问,必须携带有效 Token 才可以交互接口!