# 猿接口-Backend **Repository Path**: giteeygq/ape-interface-backend ## Basic Information - **Project Name**: 猿接口-Backend - **Description**: API调用后端基于SpringBoot、MyBatis-Plus、Dubbo、Nacos、Spring Cloud Gateway等技术实现,管理员可以接入并发布接口,统计分析各接口调用情况;用户可以注册登录并开通接口调用权限,然后可以浏览接口及在线调试,还能使用客户端 SDK 轻松在代码中调用接口。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-08-20 - **Last Updated**: 2023-09-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: SpringBoot, mybatis-plus, Dubbo, SpringCloudGateWay ## README # API-Back 猿接口平台主要功能:管理员可以接入并发布接口,统计分析各接口调用情况;用户可以注册登录并开通接口调用权限,然后可以浏览接口及在线调试,还能使用客户端 SDK 轻松在代码中调用接口。 ![架构图](README.assets/Snipaste_2023-09-01_19-15-41.png) ## 技术选型 - Java Spring Boot - MySQL 数据库 - MyBatis-Plus 及 MyBatis X 自动生成 - API 签名认证(Http 调用) - Spring Boot Starter(SDK 开发) - Dubbo 分布式(RPC、Nacos) - Swagger + Knife4j 接口文档生成 - Spring Cloud Gateway 微服务网关 - Hutool、Apache Common Utils、Gson 等工具库 ## Focal Point ### 客户端 SDK 开发人员在调用其他接口时,开发人员考虑使用HTTP调用,不仅要考虑接口地址、接口请求方式、接口请求参数、接口返回参数,同时HTTP调用方式还有多种调用方式,例如HttpClient、RestTemplate、Feign、第三方库OKHttp、Hutool等;HTTP协议发送消息是以明文的方式,不像HTTPS以对称加密、非对称加密、证书效验等方式保证数据安全。 那么开发人员如果要调用一个接口必须要兼顾以上因素,同时调用不同的接口还要不断重复造轮子,这对开发人员来说是个灾难。 所以我们基于此来开发SDK,使开发人员能轻松在代码中调用接口。 **InterfaceInfoServiceImp**中通过反射调用SDK中不同的接口 ```java //客户端SDK YiApiClient yiApiClient = new YiApiClient(accessKey, secretKey); String result = ReflectUtil.invoke(yiApiClient, methodName, args); ``` **SDK**整体结构 ```java public class YiApiClient { private static final String GATEWAY_HOST = "http://127.0.0.1:8090"; private String accessKey; private String secretKey; public YiApiClient(String accessKey, String secretKey) { this.accessKey = accessKey; this.secretKey = secretKey; } //XXX表示不同的接口 public String getXXX(String args){ HttpResponse httpResponse = HttpRequest.get(GATEWAY_HOST + "/api/botian/doutu") .addHeaders(getHeaderMap(args)) .execute(); String result = httpResponse.body(); return result; } //保证安全 private Map getHeaderMap(String body) { Map hashMap = new HashMap<>(); hashMap.put("accessKey", accessKey); hashMap.put("nonce", RandomUtil.randomNumbers(4)); hashMap.put("body", body); hashMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); hashMap.put("sign", genSign(body, secretKey)); return hashMap; } } ``` ### 用户鉴权 为了保证接口调用的安全,要对每个用户进行鉴权,防止任意用户随意调用接口浪费性能,同时黑客可以通过抓包获取到了请求的HTTP报文,然后黑客自己编写了一个类似的HTTP请求,发送给服务器。也就是说服务器处理了两个请求,先处理了正常的HTTP请求,然后又处理了黑客发送的篡改过的HTTP请求。 #### 签名认证 通过http request header头传递参数 参数1: accessKey 调用的标识(复杂,无序,无规律) 参数2: secretKey 密钥 (复杂,无序,无规律)该参数不传递,进行加密生成签名后传递 ```java public static String genSign(String body, String secretKey) { Digester sha256 = new Digester(DigestAlgorithm.SHA256); String content = body + "." + secretKey; return sha256.digestHex(content); } ``` 服务端通过accessKey从数据库获取secretKey然后一模一样的参数和算法去生成签名,只要和用户传的一致,就表示密钥一致 #### 重放攻击 重放攻击是一种网络安全攻击方式,攻击者通过记录或复制合法的通信数据,并在稍后的时间重播这些数据,以欺骗系统,获得未经授权的访问或执行操作。 解决方案:在通信中使用**时间戳和随机数**来确保通信数据的唯一性和时效性。 ```java hashMap.put("nonce", RandomUtil.randomNumbers(4)); hashMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); ``` 服务的验证 ```java String nonce = headers.getFirst("nonce"); String timestamp = headers.getFirst("timestamp"); //这里大致演示下,正常情况应该用一个set集合中验证,并在规定时间内清除 if (Long.parseLong(nonce) > 10000L) { return handleNoAuth(response); } Long currentTime = System.currentTimeMillis() / 1000; final Long FIVE_MINUTES = 60 * 5L; if ((currentTime - Long.parseLong(timestamp)) >= FIVE_MINUTES) { return handleNoAuth(response); } ``` ### API 网关 一方面,当接口每次调用都需要统计接口被调用统计信息,接口开发者需要自己去添加统计代码。例如,每个开发团队都需要自己写一个类似AOP切面的统计方法,我们开发人员还要告诉数据库统计地址,这显示是不合理的。 另一方面,当开发人员使用SDK调用接口时,请求头会添加用户鉴权需要的值,那么进行鉴权的逻辑在哪里实现?接口开发人员吗?如果是这样那么接口开发人员似乎不会在开发接口功能了。 那么如何解决这些问题呢?我们可以考虑当前大型项目中使用的微服务架构,客户端想要使用每个服务,那么必须通过网关(Gateway)来进行路由、统一业务处理才可以访问微服务。 [Spring Cloud Gateway]: https://spring.io/projects/spring-cloud-gateway "项目使用的网关技术" 项目中主要使用的网关功能: - 路由:起到转发的作用,比如有接口A和接口B,网关会记录这些信息,根据用户访问的地址和参数,转发请求到对应的接口(服务器/集群) - 统一业务处理:把每个项目中都要做的通用逻辑放到上层(网关),统一处理,比如本项目的次数统计 - 统一鉴权:判断用户是否有权限进行操作,无论访问什么接口,我都统一去判断权限,不用重复写 - 访问控制:黑白名单,比如限制ddos ip - 统一日志:将下游项目的文档进行聚合,在一个页面统一查看,主要记录每次的请求和响应 ### RPC 分布式 我们在API网关中要进行统一业务处理,比如本项目中接口调用次数统计。网关项目比较存粹,没有操作数据库的包,并且还要调用我们之前写过的代码?复制粘贴维护麻烦。怎么去操作数据库,怎么复用之前的方法? 理想情况:直接请求到其他项目的方法 有以下两种方式: **HTTP请求(HTTP Client、RestTemplate、Feign)** - 提供方提供一个接口(地址,请求方法,参数,返回值) - 调用方使用HTTP Client之类的代码包去发送HTTP请求 **RPC** - 开发人员可以像调用本地方法一样去调用远程方法 这里使用RPC更合适,因为HTTP请求需要网关设置很多额外的参数,因此后端服务之间更适合使用RPC调用远程方法。 **步骤:** 1. 配置注册中心Nacos,提供服务注册发现 2. 设置一个公共项目来定义服务接口,服务接口是Dubbo中沟通消费端和服务端的桥梁。 3. backend项目作为服务提供者(@DubboService),提供3个方法: a. 实际情况应该是去数据库中查是否已分配给用户 b. 从数据库中查询模拟接口是否存在,以及请求方法是否匹配(还可以校验请求参数) c. 调用成功,接口调用次数+1 invokeCount 4. gateway项日作为服务调用者(@DubboReference),调用这3个方法