1. 签名
为了防止请求的合法性和数据完整性,防止数据被篡改,一般我们会对接口请求数据做签名。
请求方将根据约定好的参数排序 + 时间戳 + 密钥
拼接成一个字符串,然后通过(AES
、DES
、RAS
、SM
、MD5
等)等签名算法,生成一个sign
。
在请求参数或者请求头中增加sign
参数字段,传递给API接口。 服务段接收到请求后,利用相同的签名算法和规则,重新计算签名,然后将结果与sign
参数值比较。
如果两者一致,则验证通过,否则拒绝请求。
增加时间戳和随机数的目的是为了增强请求的唯一性,同时,控制请求的时效性。
2. 加密
对于API接口
传递的一些敏感数据或隐私数据,防止第三方窃取,保证用户 的隐私。如:用户个人信息、财务记录、健康数据等。
我们可对接口的整体传输的数据进行加密,包括签名数据,将其整体加密成一个字符串。
我们可采用对称加密算法(AES
、DES
)、非对称加密算法(RSA
、ECC
、DSA
)等。公钥加密、私钥解密
。
为什么有https
了,还要对数据进行加密?
-
特定数据保护:对于特别敏感的数据,如个人健康信息、金融交易详情等,应用层可能需要实施额外的加密,以符合特定的安全标准或法规要求(如HIPAA、PCI-DSS等)。
-
细化权限控制:在多租户系统或具有复杂权限管理的场景中,可能需要对数据进行额外加密来实现更细粒度的访问控制,即使数据在HTTPS加密通道中传输,也能确保只有拥有相应解密密钥的用户或系统组件能访问数据。
-
防止逻辑漏洞:即使通信链路安全,应用程序内部的逻辑漏洞仍可能暴露数据。应用层的加密可以在一定程度上减轻由于应用程序缺陷导致的数据泄露风险。
-
长期数据保护:HTTPS提供的会话级别的加密主要针对数据在传输过程中的保护,而应用层加密有时是为了实现数据的持久化加密,确保即使数据存储或备份被非法访问,也能保持加密状态。
3. IP白名单
为了进一步加强API接口
的安全性,防止资源乱用,确保数据隐私和合规性,简化访问控制,可以设置接口访问IP白名单
。 可通过限制访问的IP
,只允许在IP白名单重的IP
可请求访问接口,否则直接返回无访问权限。IP白名单
也可以加在API网关
服务上。
但也要防止公司的内部应用服务器被攻破,这种情况也可以从内部服务器上发起API接口的请求。
这时候就需要增加web防火墙
了,比如:ModSecurity
等。
4. 限流
如果你的API接口
服务会被第三方调用了,这就意味着着接口的调用频率是没法控制的。
如果接口调用流量突然增高,可能导致接口服务超载而挂掉,不能对外提供服务。
由此,必须要对API接口
的访问量做限流,保证访问量的平稳。 一般限流做法有四种:
-
固定窗口做限流:控制同一个
ip
,在设定时间内(比如:1分钟),对API接口
总的请求次数,不能超过预设的次数(比如:1000
次)。 -
令牌桶算法限流:系统以恒定速率向“桶”中添加令牌,每个请求需要消耗一个令牌才能通过。如果桶满,多余的令牌会被丢弃;若桶空,则请求被限流。
-
漏桶算法限流:请求作为水流入桶中,桶以恒定速率漏水。如果请求速率超过漏水速率,超出部分的请求将被丢弃。
-
基于
API Gateway
的限流:利用API网关
作为统一入口,可以在网关层面实现上述任何一种限流策略,为后端服务提供保护。
5. 参数校验
需要对API接口
做参数校验,比如:校验参数格式,校验必填字段是否为空,校验字段类型,校验字段长度,校验枚举值等。避免因数据不合法导致的程序错误或异常,比如确保字符串非空、数值在合理范围内等。
通过在接口层面拦截不符合要求的数据,可以减少内部系统处理无效数据的可能性,从而提升系统的健壮性和稳定性,防止因错误数据导致的服务崩溃或性能下降。
如超长字段传入,如果不做验证,向数据库保存的时候,因为字段过长,而保存报错。
其实这个可以在入口就可做校验,没必要到数据库层面再抛异常,很浪费资源。
有很多与钱有关的金额字段,如果前期不做校验,很可能错误的数据参与计算,产生错误的结果,早晨很大损失。
我们可用框架自带的一些注解做校验(如:@Null
、@NotEmpty
、@Size
、@Max
、@Min
),也可以自定义注解:
如:验证手机号码是否正确
@Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.PARAMETER,
ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = MobileValidator.class
)
public @interface Mobile {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
校验代码如下:
public class MobileValidator implements ConstraintValidator<Mobile, String> {
@Override
public void initialize(Mobile annotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// 如果手机号为空,默认不校验,即校验通过
if (StrUtil.isEmpty(value)) {
return true;
}
// 校验手机
return ValidationUtils.isMobile(value);
}
}
6. 统一返回值
就是结果返回的结果格式相同,避免调用方对不同格式做不同的处理,增加调用方的业务处理复杂度。如: 正常返回数据`Json·格式如下:
{
"code":0001,
"message":"",
"data":[{"id":0001,"name":"吴农软语"}]
}
校验错误返回的Json
格式如下
{
"code":0002,
"message":"签名错误",
"data":""
}
系统异常返回的Json
格式如下:
{
"code":0003,
"message":"没有权限",
"data":""
}
7. 统一封装异常
对API接口
中的异常进行统一处理,避免异常直接抛给调用方。将异常堆栈信息或数据库错误信息、或错误代码和行数信息暴露给第三方,是极其危险。
不法分子里中接口异常返回值重的信息,猜出数据结构或代码逻辑,用以攻击服务,造成系统损失。
因此我们要对API接口
中的异常返回做统一处理,把异常转换成Json
格式,同正常返中系统校验失败那种,如下:
{
"code":500,
"message":"服务器内部错误",
"data":""
}
返回码code
是500
,返回信息message
是服务器内部异常。
这样第三方平台就知道是API接口
出现了内部问题,但不知道具体原因,他们可以找我们排查问题。
我们可以在内部的日志文件中,把堆栈信息
、数据库信息
、错误代码行数
等信息,打印出来。
我们可以在API gateway
中对异常进行拦截,做统一封装,然后给第三方平台的是处理后没有敏感信息的错误信息。
8. 请求日志
在第三方平台请求你的API接口时,接口的请求日志非常重要,通过它可以快速的分析和定位问题。
我们需要把API接口
的请求url
、请求参数
、请求头
、请求方式
、响应数据
和响应时间
等,记录到日志文件中。
最好有TraceId
,可以通过它串联整个请求的日志,过滤多余的日志。
当然有些时候,请求日志不光是你们公司开发人员需要查看,第三方平台的用户也需要能查看接口的请求日志。
这时就需要把日志落地到数据库,比如:Mongodb
或者Elastic search
,然后做一个UI
页面,给第三方平台的用户开通查看权限。这样他们就能在外网查看请求日志了,他们自己也能定位一部分问题。
9. 幂等设计
接口极有可能在极短的时间内,同一次请求,多次访问,比如:在1秒内请求两次。有可能是他们业务系统有BUG
,或者在做接口调用失败重试,因此我们的API接口
需要做幂等设计。
也就是说要支持同一次业务处理请求多次调用API接口
。如新增数据,第一次请求数据库会新增数据,但第二次请求以后就不会新增数据,但会返回成功。目的是减少错误数据产生。
10. 限制记录条数
对于对我提供的批量接口,一定要限制请求的记录条数。
如果请求的数据太多,容易形成大对象,很容易造成API接口响应超时等问题,让API接口变得不稳定。
可以事先预设一个接口一次能返回的最大值,比如:500条
,这个参数可设定为可配置,然后根据实际运行情况进行设定。
11. 异步处理
API接口
调用的时候,我们都是同步处理,请求处理完之后立刻返回结果,在同一次请求中完成结果的返回。
但当API接口
处理的业务逻辑比较复杂时,接口耗时比较长,特别是有些批量接口,如果采用同步方式,可能存在超时的情况,甚至可能拖垮对方服务。
我们可以改成异步处理方式来提升API接口
的请求性能。
常规的做法采用MQ
(RabbitMQ
、Kafka
等)异步解耦的方式。首先,当调用接口的时候,先给MQ
发一条消息,然后直接返回结果。MQ
消费这条消息,并调用对应的业务处理逻辑进行业务处理。业务逻辑处理完毕后,结果由两种方式通知给调用方:
第一:直接调用调用方的通知接口,将结果推送给调用方。
第二:调用方采用定时轮训的方式,如果结果处理完毕,就返回结果,没有就返回处理中,轮训知道结果获取到结束。
12. 数据脱敏
有时候第三方平台调用我们API接口时,获取的数据中有一部分是敏感数据,比如:用户手机号、银行卡号等等。
这样信息如果通过API接口
直接保留到外网,是非常不安全的,很容易造成用户隐私数据泄露的问题。
这就需要对部分数据做数据脱敏了。
我们可以在返回的数据中,部分内容用*
号代替。
用户手机号:139****927
。
这样即使数据被泄露了,也只泄露了一部分,不法分子拿到这份数据也没啥用。