# api_open_platform **Repository Path**: pan_shengdong/api_open_platform ## Basic Information - **Project Name**: api_open_platform - **Description**: ​ 本项目是基于React + Spring Boot + Dubbo +Gateway的API接口开放调用平台。管理员可以接入并发布接口,可视化各个接口的调用情况;用户可以开通接口调用权限、浏览接口、在线调试,并通过引入客户端SDK的方式,轻松调用接口。 ​ 本项目的核心功能在于,为注册的用户分配Access Key和Secret Key,接口调用方通过引入客户端SDK的方式,用Secre - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2025-07-18 - **Last Updated**: 2025-11-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目简介 ## 项目简介: ​ 本项目是基于React + Spring Boot + Dubbo +Gateway的API接口开放调用平台。管理员可以接入并发布接口,可视化各个接口的调用情况;用户可以开通接口调用权限、浏览接口、在线调试,并通过引入客户端SDK的方式,轻松调用接口。 ​ 本项目的核心功能在于,为注册的用户分配Access Key和Secret Key,接口调用方通过引入客户端SDK的方式,用Secret Key+body形成自定义签名,将签名发送到服务提供方,服务提供方通过校验签名是否正确,判断该接口调用是否有效,从而提供服务。 ## 项目构成 1、interfaceProject:作为接口提供者。 2、open-api-ui:前端UI界面 3、panda-openapi-backend:后台管理系统 4、panda-openapi-common:公共服务模块 5、panda-openapi-gateway:网关模块 6、panda-openapi-sdk:自定义SDK ## 主要工作: 1、使用Spring Boot脚手架+Ant Design Pro脚手架快速搭建前后端项目。 2、后端基于Mybatis Plus 结合 Mybatis X的插件,实现CRUD,减少工作量。 3、前端利用swagger + Kinfe4J + openapi生成前端接口调用代码,降低前后端协作成本。 4、为了防止接口被恶意调用,涉及API签名认证算法,为用户分配access key和secret key鉴权,保障调用的安全性。 5、为解决服务调用方调用成本过高问题,基于Spring Boot Starter开发客户端SDK,供调用方使用。 6、选用Spring Cloud Gateway作为API网关,实现了路由转发、访问控制、流量染色、并抽取并引入common模块,集中处理签名校验、 接口调用统计等功能。 7、整合nacos+dubbo,实现远程接口调用功能。 # API签名认证 accesskey:相当于账户名称 accessSecret相当于账户密码,在接口调用过程中,不进行传参调用。【具体使用方式,配置好了(accessKey,accessSecret),使用服务方提供的sdk,会生成签名,传输的是签名,不是accessSecret】 ## 服务方 ​ 服务方本身就保存着客户方的(accessKey和accessSecret),当客户方调用时,自动调用sdk生成签名,此时服务方在服务器上再次生成签名,两者签名对比下,即可判断请求是否合法。 ### 1、写好接口 ```java @PostMapping("/getUserNameByPostWithSign") public String getUserNameByPostWithSign(@RequestBody User user, HttpServletRequest request) { // 1.拿到这五个我们可以一步一步去做校验,比如 accessKey 我们先去数据库中查一下 // 从请求头中获取参数 String accessKey = request.getHeader("accessKey"); String nonce = request.getHeader("nonce"); String timestamp = request.getHeader("timestamp"); String sign = request.getHeader("sign"); String body = request.getHeader("body"); if (!accessKey.equals("panda")) { throw new RuntimeException("无权限"); } // 校验随机数,模拟一下,直接判断nonce是否大于10000 if (Long.parseLong(nonce) > 10000) { throw new RuntimeException("无权限"); } // 4.校验时间戳与当前时间的差距,交给大家自己实现 // if (timestamp) {} String serverSign = SignUtil.genSign(body, "abcdefg"); if(!sign.equals(serverSign)){ throw new RuntimeException("签名不对,无权限"); } return "POST 用户名字是" + user.getUsername(); } ``` ### 2、新项目-生成sdk ```java @Configuration @ConfigurationProperties("panda.sdk.client") @Data @ComponentScan public class PandaSdkClientConfig { private String accessKey; private String secretKey; @Bean public PandaApiClient pandaApiClient(){ return new PandaApiClient(accessKey,secretKey); } } ``` panda-openapi-sdk/src/main/resources/META-INF/spring.factories ``` org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.panda.sdk.PandaSdkClientConfig ``` ### 3、为接口适配客户端请求 ```java package com.panda.sdk.client; import cn.hutool.core.util.RandomUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.panda.sdk.model.User; import com.panda.sdk.utils.SignUtil; import java.util.HashMap; import java.util.Map; /** * @Author pansd * @Date 2025-07-31 13:40 * @Des */ public class PandaApiClient { private String accessKey; private String secretKey; public PandaApiClient(String accessKey, String secretKey) { this.accessKey = accessKey; this.secretKey = secretKey; } // 创建一个私有方法,用于构造请求头 private Map getHeaderMap(String body) { // 创建一个新的 HashMap 对象 Map hashMap = new HashMap<>(); // 将 "accessKey" 和其对应的值放入 map 中 hashMap.put("accessKey", accessKey); // 将 "body" 和其对应的值放入 map 中 hashMap.put("body", body); hashMap.put("nonce", RandomUtil.randomNumbers(4)); hashMap.put("timestamp", String.valueOf(System.currentTimeMillis())); hashMap.put("sign", SignUtil.genSign(body, secretKey)); // 返回构造的请求头 map return hashMap; } public String getUserNameByPostWithSign(User user) { String json = JSONUtil.toJsonStr(user); HttpResponse httpResponse = HttpRequest.post("http://localhost:8856/api/username/getUserNameByPostWithSign") // 添加前面构造的请求头 .addHeaders(getHeaderMap(json)) .body(json) .execute(); System.out.println(httpResponse.getStatus()); String result = httpResponse.body(); System.out.println(result); return result; } } ``` #### 生成签名方法 ```java package com.panda.sdk.utils; import cn.hutool.crypto.digest.DigestAlgorithm; import cn.hutool.crypto.digest.Digester; /** * @Author pansd * @Date 2025-07-31 14:22 * @Des */ public class SignUtil { /** * 生成签名 * @param body 包含需要签名的参数的哈希映射 * @param secretKey 密钥 * @return 生成的签名字符串 */ public static String genSign(String body, String secretKey) { // 使用SHA256算法的Digester Digester md5 = new Digester(DigestAlgorithm.SHA256); // 构建签名内容,将哈希映射转换为字符串并拼接密钥 String content = body + "." + secretKey; // 计算签名的摘要并返回摘要的十六进制表示形式 return md5.digestHex(content); } } ``` ### 4、执行install 生成dependence依赖 ## 调用方 ​ 引用服务方提供的sdk,配置好accessKey和accessSecret后,进行接口调用,在sdk中会自动生成签名,传输到服务方的服务器上。 ### 1、引入sdk ```xml com.panda panda-openapi-sdk 1.0.3 ``` ### 2、配置 ```yaml server: port: 8856 servlet: context-path: /api panda: sdk: client: access-key: panda secret-key: abcdefg ``` ### 3、调用 ```java @SpringBootTest class PandaApiClientTest { @Resource private PandaApiClient pandaApiClient; @Test void getNameByGet() { User user = new User(); user.setUsername("panda"); String userNameByPostWithSign = pandaApiClient.getUserNameByPostWithSign(user); System.out.println("userNameByPostWithSign="+userNameByPostWithSign); } } ``` ## 签名生成方式: 将用户请求的form表单和accessSecret进行编码 ```java public static String genSign(String body, String secretKey) { Digester md5 = new Digester(DigestAlgorithm.SHA256); String content = body + "." + secretKey; return md5.digestHex(content); } ``` # Dubbo整合nacos ## 踩坑日记: dubbo的服务方和调用方,接口路径必须在同一个路径下。 ## 服务方整合dubbo+nacos ```xml org.apache.dubbo dubbo 3.0.9 com.alibaba.nacos nacos-client 2.1.0 ``` ```yaml # 以下配置指定了应用的名称、使用的协议(Dubbo)、注册中心的类型(Nacos)和地址 dubbo: application: # 设置应用的名称 name: dubbo-springboot-demo-provider # 指定使用 Dubbo 协议,且端口设置为 -1,表示随机分配可用端口 protocol: name: dubbo port: 22221 registry: # 配置注册中心为 Nacos,使用的地址是 nacos://localhost:8848 id: nacos-registry address: nacos://localhost:8848 ``` ```java public interface DemoService { String sayHello(String name); String sayHello2(String name); default CompletableFuture sayHelloAsync(String name) { return CompletableFuture.supplyAsync(() -> sayHello(name)); } } @DubboService public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { System.out.println("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); return "Hello " + name; } @Override public String sayHello2(String name) { return "yupi"; } } @SpringBootApplication(exclude = {RedisAutoConfiguration.class}) @MapperScan("com.panda.apiopen.mapper") @EnableScheduling @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) @EnableDubbo public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } } ``` ## 调用方整合dubbo+nacos ```xml org.apache.dubbo dubbo 3.0.9 com.alibaba.nacos nacos-client 2.1.0 ``` ```java # 以下配置指定了应用的名称、使用的协议(Dubbo)、注册中心的类型(Nacos)和地址 dubbo: application: # 设置应用的名称 name: dubbo-springboot-demo-caller # 指定使用 Dubbo 协议,且端口设置为 -1,表示随机分配可用端口 protocol: name: dubbo port: -1 registry: # 配置注册中心为 Nacos,使用的地址是 nacos://localhost:8848 id: nacos-registry address: nacos://localhost:8848 ``` ```java public interface DemoService { String sayHello(String name); String sayHello2(String name); default CompletableFuture sayHelloAsync(String name) { return CompletableFuture.completedFuture(sayHello(name)); } } @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) @EnableDubbo @Service public class PandaOpenapiGatewayApplication { @DubboReference private DemoService demoService; public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(PandaOpenapiGatewayApplication.class, args); PandaOpenapiGatewayApplication application = context.getBean(PandaOpenapiGatewayApplication.class); String result = application.doSayHello("world"); String result2 = application.doSayHello2("world"); System.out.println("result: " + result); System.out.println("result: " + result2); } public String doSayHello(String name) { return demoService.sayHello(name); } public String doSayHello2(String name) { return demoService.sayHello2(name); } } ``` # 前端React总结 1、这是我的第一个react项目,之前都是写的vue,所以总结一下本项目react的用法。 ## 页面构成 1、引入代码:import 2、主体代码:const InterfaceAnalysis: React.FC = () => {} - 使用 usetState,进行定义值和改变值 const [data, setData] = useState([]); - ```react //使用useEffect来进行页面初始化 useEffect(() => { try { listTopInvokeInterfaceInfoUsingGet().then(res => { if (res.data) { setData(res.data); } }) } catch (e: any) { } }, []) ``` - return里边写页面元素标签{} 相当于vue里边的{{}} - ()相当于使用标签 ```react return ( {/* 使用 ReactECharts 组件,传入图表配置 */} ); ``` 3、导出代码:export default InterfaceAnalysis;