012-从零搭建微服务-接口文档(二)

news2024/12/24 9:15:59

写在最前

如果这个项目让你有所收获,记得 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: true
        is-implicit: true
        is-password: true
        is-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 {

	/**
	 * 文档基本信息
	 */
	@NestedConfigurationProperty
	private InfoProperties info = new InfoProperties();

	/**
	 * 组件
	 */
	@NestedConfigurationProperty
	private Components components = null;

	/**
	 * 是否开启 swagger
	 */
	private Boolean enabled = true;

	/**
	 * 网关
	 */
	private String gateway;

	/**
	 * 服务转发配置
	 */
	private Map<String, String> services;

	/**
	 * 文档的基础属性信息
	 *
	 * @see io.swagger.v3.oas.models.info.Info
	 */
	@Data
	public static class InfoProperties {

		/**
		 * 标题
		 */
		private String title = null;

		/**
		 * 描述
		 */
		private String description = null;

		/**
		 * 联系人信息
		 */
		@NestedConfigurationProperty
		private Contact contact = null;

		/**
		 * 许可证
		 */
		@NestedConfigurationProperty
		private 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;

	@Bean
	public 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();

		@Data
		public 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;

	@Override
	public 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 控制是否开启接口文档密码校验,通过 usernamepassword 配置登录接口文档的用户名与密码

swagger:
  basic:
    # 是否开启接口文档密码校验
    enabled: true
    username: mingyue
    password: mingyue

启动测试

打开 swagger-ui: http://mingyue-gateway:9100/swagger-ui.html,会弹出登录框,输入 Nacos 中配置的用户名密码登录即可,查看是否配置成功!

image-20230623163859415

小结

Swagger 接口文档基础功能已经可以使用,但仍有很多很多需要做的地方,比如:

  1. Authorize 功能,也就是 Token 还未使用;
  2. 基于 Openapi 结构体接入第三方工具,如:ApifoxPostman等。为什么有 Swagger-UI ,要接入第三方工具?其实 Swagger-UI 很不好用,哈哈哈~;
  3. 导出接口文档,如PDF等文本格式;
  4. 。。。

山高路远,但仍要脚踏实地,收回来!下一篇我们先控制接口访问,必须携带有效 Token 才可以交互接口!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/677807.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

统一拦截--过滤器Filter

1.过滤器Filter 1. 概述 概念: Filter过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、Listener)之一。过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。过滤器一般完成一些通用的操作&#xff0c;比如:登录校验、统一编码处理、敏感字符处理等…

Tcp协议的十大特性详解+示例

前言 之前我们简单了解了一下Tcp是什么及它的套接字如何使用:基于UDP和TCP套接字实现简单的回显客户端服务器程序_Crystal_bit的博客-CSDN博客 因为要给大家介绍Tcp的十大特性&#xff0c;所以这里给出Tcp报头结构&#xff1a; 目录 1. 确认应答 2. 超时重传 3. 连接管理 3…

【Android复习笔记】Parcelable 为什么速度优于 Serializable ?

Q:Parcelable 为什么速度优于 Serializable ? 首先,抛开应用场景谈技术方案都是在耍流氓,所以如果你遇到有面试官问这样的题目本身就是在给面试者挖坑。 序列化 将实例的状态转换为可以存储或传输的形式的过程。 Serializable 实现方式: Serializable 是属于 Java 自带的…

Solid Converter PDF v10 安装及使用教程

目录 一、软件介绍二、下载教程三、安装教程四、使用教程1.PDF转Word、Html等2.合并PDF文件 一、软件介绍 Solid Converter PDF是一套专门将PDF文件转换成Word的软件。 能够将PDF转换为Word、Excel、HTML、PowerPoint、纯文本文件从PDF文档中提取数据并以CSV等格式保存能够转…

数仓工程师理解复杂业务的思考方法论

模型设计框架&#xff08;业务过程驱动&#xff09;还是在经典的三层数据模型架构下去进行&#xff0c;概念模型、逻辑模型、物理模型 首先概念模型其实是业务过程&#xff08;流程图&#xff09;&#xff0c;其中需要考虑到几个方面&#xff1a; 1.数据 业务覆盖 业务感知、…

循坏队列CircularQueue

前言 一、CircularQueue 二、特点 三、设计思路 1&#xff09;判空与判满 2&#xff09;链表还是数组实现&#xff1f; 四、实现 1).IsEmpty() 2).IsFull() 3)CircularQueueCreate创建 4&#xff09;CircularQueueEnQueue插入 5&#xff09;CircularQueueDeQueue删除 6&#xf…

React Hook之useCallback 性能优化

上文 对比之前的组件优化说明React.memo的作用我们说了 React.memo的妙用 但是 它却并非万能 我们来看这个情况 我们子组件代码编写如下 import React from "react";const ChildComponent ({ dom1funt }) > {console.log("ChildComponent 被重新渲染"…

规则引擎--规则集:规则集合的组织和执行

目录 回顾easy-rules的rules执行如何想规则集合的构造 规则集合定义普通规则集和执行定义树形规则集 当弄清楚了一个规则的设计和执行逻辑后&#xff0c;接下来需要考虑的就是许多的规则如何组织了&#xff0c;即规则集的抽象设计。 来看一些例子 回顾easy-rules的rules执行 …

NFCEE Discovery and Mode Set

10.1 NFCEE ID NFCC 动态为 NFCEE 分配 ID&#xff08;称为“NFCEE ID”&#xff09;。 DH 通过执行 NFCEE Discovery 来了解 ID 值。 在配置状态为 0x01 的 NFCC 重置之前&#xff0c;NFCEE ID 一直有效。 值为 0x00 的 ID 在本规范中称为 DH-NFCEE ID&#xff0c;并且应代表…

五、Docker本地镜像发布到阿里云/发布到私有库

目录 前言一、本地镜像发布到阿里云1.1 流程图1.2 注册阿里云创建容器服务个人实例1.3 创建命名空间1.4 创建镜像仓库1.5 将镜像推送到阿里云本地仓库 二、从阿里云仓库拉去自己推送的镜像三、本地镜像发布到阿里云总结四、本地镜像发布到私有库4.1 流程图4.2 下载镜像Docker R…

Shell编程从入门到实践——实践篇

欢迎关注 「Android茶话会」 回 「学习之路」 取Android技术路线经典电子书回 「pdf」 取阿里&字节经典面试题、Android、算法、Java等系列武功秘籍。回 「天涯」 取天涯论坛200精彩博文,包括小说、玄学等 背景 之前在搞一些CI/CD,使用到了shell脚本&#xff0c;shell的开…

nvdiffrec在Windows上的配置及使用

nvdiffrec是NVIDIA研究院开源的项目&#xff0c;源代码地址&#xff1a;https://github.com/NVlabs/nvdiffrec &#xff0c;论文为《Extracting Triangular 3D Models, Materials, and Lighting From Images》&#xff0c;从图像中提取三角形三维(三角网格)模型、空间变化的材质…

uni-app微信小程序获取手机号授权登录(复制即用,js完成敏感数据对称解密,无需走服务端处理)

目录 一、示例 二、具体实现说明 一、示例 获取到的手机号 二、具体实现说明 属性说明 属性名说明生效时机getphonenumber获取用户手机号回调open-type"getPhoneNumber" 按钮写法 <template><view class"login"><view class"content…

为什么要写这个带点玄幻气息的英语单词记忆博客

&#x1f31f;博主&#xff1a;命运之光 ☀️专栏&#xff1a;英之剑法&#x1f5e1; ❤️‍&#x1f525;专栏&#xff1a;英之试炼&#x1f525; ☀️博主的其他文章&#xff1a;点击进入博主的主页 &#x1f433; 开篇想说的话&#xff1a;开学就大三了&#xff0c;命运之光…

DMA详解及应用(嵌入式学习)

DMA 0. 前言1. DMA作用2. DMA特性3. DMA寄存器4. DMA的增量或者循环模式5. 练习 0. 前言 DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;是一种计算机系统中用于高效地实现数据传输的技术。它允许数据在外设和内存之间直接传输&#xff0c;而无需C…

GEE:为每个对象(斑块/超像素)添加属性

作者:CSDN @ _养乐多_ 本文将介绍为每个对象(斑块/超像素)添加属性的代码。并举例将最近距离作为属性添加到每个对象(斑块/超像素)特征中。 结果如下图所示, 文章目录 一、代码二、代码链接一、代码 这段代码的目的是对动态世界土地覆盖图像进行分析,并提取出其中的目…

贪婪算法简介-数据结构和算法教程

贪婪算法是一种算法范例&#xff0c;它遵循在每个阶段进行局部最优选择的问题求解启发式&#xff0c;希望找到全局最优值。换句话说&#xff0c;贪婪算法在每一步都选择最好的可能选项&#xff0c;而不考虑该选择对未来步骤的影响。 当一个问题可以被划分成更小的子问题&#…

1.GPIO的工作原理

1.stm32引脚说明&#xff1a; 对于stm32f103zet6&#xff1a; 一共有7组io口&#xff1b;每组io口有16个io&#xff1b;一共有16*7112个io&#xff1b;分组情况为&#xff1a;GPIOA&#xff0c;GPIOB~GPIOG&#xff1b; 2.GPIO的基本结构&#xff1a; 3.GPIO的工作模式&…

C++入门:类和对象(后)

目录 前言&#xff1a; 一&#xff1a;static成员 (1)概念 (2)特性 (3)例子 二&#xff1a;explicit关键字 三&#xff1a;内部类 (1)概念 (2)特性 (3)实例 四&#xff1a;匿名对象 (1)概念 (2)特性 (3)实例 五&#xff1a;拷贝对象时的一些编译器优化 (1)引入 …

Spring整合MyBatis底层原理

Spring整合MyBatis底层原理 项目结构图 项目代码 build.gradle需要进入的依赖 // testImplementation(platform("org.junit:junit-bom:5.9.1")) // testImplementation("org.junit.jupiter:junit-jupiter")implementation("org.aspectj:aspect…