猿接口平台主要功能:管理员可以接入并发布接口,统计分析各接口调用情况;用户可以注册登录并开通接口调用权限,然后可以浏览接口及在线调试,还能使用客户端 SDK 轻松在代码中调用接口。
开发人员在调用其他接口时,开发人员考虑使用HTTP调用,不仅要考虑接口地址、接口请求方式、接口请求参数、接口返回参数,同时HTTP调用方式还有多种调用方式,例如HttpClient、RestTemplate、Feign、第三方库OKHttp、Hutool等;HTTP协议发送消息是以明文的方式,不像HTTPS以对称加密、非对称加密、证书效验等方式保证数据安全。
那么开发人员如果要调用一个接口必须要兼顾以上因素,同时调用不同的接口还要不断重复造轮子,这对开发人员来说是个灾难。
所以我们基于此来开发SDK,使开发人员能轻松在代码中调用接口。
InterfaceInfoServiceImp中通过反射调用SDK中不同的接口
//客户端SDK
YiApiClient yiApiClient = new YiApiClient(accessKey, secretKey);
String result = ReflectUtil.invoke(yiApiClient, methodName, args);
SDK整体结构
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<String, String> getHeaderMap(String body) {
Map<String, String> 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 密钥 (复杂,无序,无规律)该参数不传递,进行加密生成签名后传递
public static String genSign(String body, String secretKey) {
Digester sha256 = new Digester(DigestAlgorithm.SHA256);
String content = body + "." + secretKey;
return sha256.digestHex(content);
}
服务端通过accessKey从数据库获取secretKey然后一模一样的参数和算法去生成签名,只要和用户传的一致,就表示密钥一致
重放攻击是一种网络安全攻击方式,攻击者通过记录或复制合法的通信数据,并在稍后的时间重播这些数据,以欺骗系统,获得未经授权的访问或执行操作。
解决方案:在通信中使用时间戳和随机数来确保通信数据的唯一性和时效性。
hashMap.put("nonce", RandomUtil.randomNumbers(4));
hashMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
服务的验证
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);
}
一方面,当接口每次调用都需要统计接口被调用统计信息,接口开发者需要自己去添加统计代码。例如,每个开发团队都需要自己写一个类似AOP切面的统计方法,我们开发人员还要告诉数据库统计地址,这显示是不合理的。
另一方面,当开发人员使用SDK调用接口时,请求头会添加用户鉴权需要的值,那么进行鉴权的逻辑在哪里实现?接口开发人员吗?如果是这样那么接口开发人员似乎不会在开发接口功能了。
那么如何解决这些问题呢?我们可以考虑当前大型项目中使用的微服务架构,客户端想要使用每个服务,那么必须通过网关(Gateway)来进行路由、统一业务处理才可以访问微服务。
项目中主要使用的网关功能:
路由:起到转发的作用,比如有接口A和接口B,网关会记录这些信息,根据用户访问的地址和参数,转发请求到对应的接口(服务器/集群)
统一业务处理:把每个项目中都要做的通用逻辑放到上层(网关),统一处理,比如本项目的次数统计
统一鉴权:判断用户是否有权限进行操作,无论访问什么接口,我都统一去判断权限,不用重复写
访问控制:黑白名单,比如限制ddos ip
统一日志:将下游项目的文档进行聚合,在一个页面统一查看,主要记录每次的请求和响应
我们在API网关中要进行统一业务处理,比如本项目中接口调用次数统计。网关项目比较存粹,没有操作数据库的包,并且还要调用我们之前写过的代码?复制粘贴维护麻烦。怎么去操作数据库,怎么复用之前的方法?
理想情况:直接请求到其他项目的方法
有以下两种方式:
HTTP请求(HTTP Client、RestTemplate、Feign)
RPC
这里使用RPC更合适,因为HTTP请求需要网关设置很多额外的参数,因此后端服务之间更适合使用RPC调用远程方法。
步骤:
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。