这是系统架构的第二篇。目的嘛:一来帮助自己理清网关架构,二来将自己觉得好的架构分享出来给大家。
本文主要讨论:
API网关业务域:统一接入、安全防护、流量管控、协议转换
API网关核心指标:安全、高可用、高并发、方便扩展、方便运维
API网关架构:系统领域划分、防护层、接入层、核心层架构。
API网关的设备安全解决方案。
一 API网关业务域
API网关作为内外的桥梁;对外通过暴露HTTP接口提供服务;对内管理所有业务系统对外暴露的接口,并将请求分发到内部各个业务系统。
1 业务域
2 统一接入
作为对外的门面,网关应该拥有统一且简洁的规范,以减少接入的成本。同时这也可以体现出专业化的技术能力。
统一的规范应该至少包括以下内容:
统一的编码规则。
统一的返回数据的结构。
统一的错误申明与处理方式。
统一的公共参数,例如识别用户身份的token,识别渠道来源的source等。
统一的加签、加解密方式;统一的身份认证或安全体系。
3 安全防护
作为请求的入口,是保护内部系统的第一道屏障,当遭受攻击时要尽可能的将影响降为最低。所以应该具备清洗恶意攻击的流量,能够在流量异常的时候屏蔽这些异常请求,或者对这些请求进行限制。
验证请求信息,屏蔽非法请求,确保接口的安全性,保证所有达底层业务系统的请求都是安全、可靠地。
常用的手段有:
防篡改,例如参数加签。
请求安全:请求端加密,网关层解密。
身份校验:必须是合法的用户,必须是合法的第三方。
接口安全级别。例如:接口不需要登录即可访问;需要登录方可访问;接口仅提供给app,H5不能使用等
接口权限限制。
黑白名单。
HTTPS。
4 流量管控
系统的承受能力都有限,例如:当营销活动做得好时,系统请求量超过系统的服务能力的极限,如果放任不管,势必压垮内部系统,此时应该对内部系统提供一定的保护。
作为服务能力的出口,一旦不可用,在外部看来所有的服务都不可用,所以应该网关必须高可用、高并发。
常见的手段有:限流、降级、熔断
5 协议转换
对外提供的都是HTTP接口,但内部系统是使用Dubbo来相互调用的,所以网关势必要能够完成协议转换,并通过负载均衡等手段将请求高效的分发到内部系统中去。
6 其他业务
1) 接口文档管理
业务发展变化势必引起服务的增加或者扩展,如果能够统一的对文档进行维护,势必会降低开发、联调、定位问题成本。我们的做法是通过api规范自动生成接口文档。
2) 调试工具和示例
当接入方很多,提供一个调试工具或者提供示例将会极大的简化接入成本。
3) SDK自动生成能力
为客户端(Android、IOS或者第三方调用者)生成SDK,简化接入、降低对方开发成本。
4) API增强
参数注入。例如注入userId、来源、ip等。
合并多个请求。例如:在一个页面中可能需要访问多个接口才能拿到所有的数据,此时为了方便调用方使用,网关可以提供合并多个接口的能力,让调用方同时发起多个http请求。
二 API网关核心指标
1 模型
2 安全性
内容见“安全防护”部分。
3 高并发性
作为企业级API的入口,所有接口都通过网关,流量可想而知是非常大的。
常见的手段有:
多级缓存
应用级缓存:堆缓存、磁盘缓存;
分布式缓存:热点数据缓存
并发:线程池;请求缓存;请求合并
4 高可用性
网关故障时,在外部来看就是整个系统不可用;所以网关应该做到7*24小时稳定运行;能够自动伸缩;API热更新;对做到接口级别、应用级别的限流和降级;支持负载均衡;支持多机房;(准)实时的系统监控;应该有一定的隔离性,避免单点故障引起雪崩。
5 扩展性性
网关纵向扩展性:企业的服务能力、安全性随业务的发展而变化,所以网关要能随着业务发展灵活的调整。
网关横向扩展性:网关需要能应对业务发展而带来的流量激增,至少紧急是可以通过增加机器带来成比例的增加服务能力。
网关应该避免频繁调整。
6 运维成本
升级发布网关、新服务的接入应该简单,方便。
三 API网关架构
1 几个要点
要支持高并发、高可用。
不必要、非安全的请求不要到下游服务中去。
网关不负责任何业务相关内容,仅负责协议转换、请求转发。
接口要符合规范;文档与接口应同步。
2 网关整体模块
3 系统领域划分
防护层
负责安全与流控;保障系统安全;可以直接在nginx层编码,例如openresty。
接入层
负责统一接入相关的所有内容。
核心层
负责请求验证、API增强、协议转换、负载均衡等。另外核心层也可以包含限流、降级、熔断功能。
4 系统分层模型
服务提供者将服务注册到网关
防护层作为网关的第一入口;过滤掉非法、无法处理的请求后,将请求交给核心层处理。
核心层转换协议通过Dubbo调用服务提供者接口,然后将调用数据沉淀,为防护层限流、降级、熔断等提供数据支撑。
5 防护层
防护层主要业务域是:限流、降级、熔断;核心目标是保证后端系统不会大流量、异常流量击垮。
防护层是入口,从性能上考虑,完全可以通过在nginx层实现,例如通过openresty实现。架构如下:
6 接入层
作为统一接入方,网关应该提供API规范,以保证对API的统一性。
1) 接入规范
a) 模型
b) 规范
API组
用于对api进行归类,限制错误码范围等。
API方法
对应于每一个API接口,包括:名称、描述、使用范围、安全级别、状态、适用范围。
公共参数
例如:token、sign、渠道、format、方法、应用等。
业务参数
名称、是否必填、默认值、是否加密等
错误码声明
类、属性注释
2) 接入
服务提供者,根据规范定义接口,然后接入到网关,网关进行校验,校验通过则生成文档、SDK、RPC实例等。
3) 文档
通过注解来定义规范,其中非常重要第一点就是生成客户端调用文档,并且文档应该和接入的接口同步更新。因为接入时解析注解,所以很容易做到这一点。
7 核心层
1) 验证层
验证层主要保证交付给业务系统的接口都是合法、有效的。
安全验证分为:常规型(sign验证)、应用型(Appkey)、用户型(token)、接口型(安全级别、参数必填、非空等)四种验证;这四种分别对应不同层次,用于不同目的。
2) 增强层
增强层主要提供API增强功能,例如:支持合并多个api请求;请求参数注入等。
3) 协议转换
将外部的HTTP接口转换成内部的Dubbo接口。
8 高并发、高可用
高并发、高可用技术以后找机会详细分析,这里暂不详细讨论。
从上面的领域划分、系统分层中可以看到,每一层都是可以分布式集群部署,并且可以水平扩展的,最不济的情况是流量激增的时候等比例的增加服务器。
四 安全
目前有很多的成熟的安全策略,他们足以应对绝大多数公司的安全需要;使用他们的时候需要清晰每种策略的用途以及适用范围。在这里主要介绍常用的几种策略,如:通用安全策略、设备级安全、应用级安全、接口级安全、用户级别安全来介绍几种。
1 核心要素
衡量一个系统是否安全主要有两个核心要素:
破解的难度。
破解后危害范围有多大。
对于开发者而言,通过抓包获取请求的参数,通过分析javascript的逻辑或者反编译app来分析app的逻辑,两方面结合起来就可以伪造用户请求,面对这种困境,我们应该尽可能增大破解难度,降低破解后对系统的危害以及影响面。
2 安全策略
1) 安全策略
2) 通用安全策略
a) 请求信息加签
请求参数加签的最重要目的是实现请求数据防篡改功能。主要用于防止通过修改请求数据伪造请求,攻击系统、破坏用户数据的行为。
一般而言在H5、app中,加签逻辑都可以通过(反编译代码)分析代码来获悉,一般仅仅将加签作为请求防篡改的手段。
如果是服务端A向服务端B发送请求,因为服务端代码部署在内网,他人无法获知其中逻辑,此时可以通过两端约定一个字符串,加签的时候使用此字符串,但是请求时不传递次字符串,服务端B收到请求以后,同样在加签的时候使用此字符串,由此可以实现请求防篡改、请求参数安全的目的。个人并不是很赞同这种做法,主要原因是将请求参数防篡改和请求参数安全两者混淆了,并且因为两端约定的字符串恒定不变,一旦被人破译出此字符串,将导致他人可以完全伪造此参数。
b) 防止请求重复提交
相同的请求多次提交到服务端,在大多数场景中(并不是所有场景中)都不是正常的行为,此时应该处理掉这些重复的请求。常见的处理手段有:客户端提交一次请求以后禁用按钮;客户端进入页面时在页面中保存一个随机字符串,服务端同时受到的请求中含有相同的此值时,忽略后一个请求;服务端防并发处理等等。
c) 注入攻击
最常见的就是SQL注入了。
d) Cookie劫持
第三方通过javascript获取、修改Cookie的信息。服务端通过httponly模式来设置cookie可以解决这个问题。
e) 混淆打包
混淆打包主要针对app、javascript,用于增大他人破解难度。
3) 设备级安全
设备级安全主要是用于防止第三方通过反编译app、分析javascript代码,解析出系统安全规则,从而完成系统攻击的目的。
后文将介绍一种保障App的设备级安全的方案。
4) 应用级安全
常见的场景有:营销活动中,一个用户只能参加一次活动,如一个用户只能秒杀一个商品;同一个ip最多能只能参加10次抽奖。使用验证码接收器,通过虚拟号注册app,刷奖励;同一个ip(某一个时间段内)的请求量不能超过N,防止并发攻击;等等。
这些场景与具体的业务相关,而且很多时候规则也会各有不同,通常使用分布式锁、风控等手段一起这些问题。
5) 接口级安全
常见的场景有:接口的参数、返回结果是否需要加密;接口有只有在登录状态时才能访问还是任何时候都能访问;接口是否只是针对app的还是app、h5、web都可以访问;请求中是否包含了接口要求的所有参数;参数格式是否符合要求等等。
6) 用户级别安全
常见的场景有:是否拥有某一个菜单的权限;是否必须登录或授权才能使用某部分功能;是否允许自己的信息被他人查看;等等。
3 企业级设备安全解决方案
没有最好的方案,只有最适合的方案。设计安全方案时要充分考虑公司的安全级别、业务诉求、开发人员水平、使用复杂度等。
以下是之前使用过的一种安全解决方案。这里仅讨论RSA加密的方式,其实也支持AES加密,流程比较简单,所以就不做讨论。
1) 设备注册
app第一次打开的时候进行设备注册,其核心目标是为了生成device token和证书。
device token是设备token,通过它可以解析出是哪一台设备。
证书中包含RSA加密的公钥,如果请求参数需要加密,客户端可以通过此公钥进行加密。
2) 用户登录
登录流程其实是获取user token的过程;这样做的目的是:这里还将用户信息和设备信息绑定在一起了,可以做到一台设备一个用户;可以很简单的实现设备互踢,即一个用户只能在一个设备互踢。
3) HTTP请求处理
收到HTTP请求以后,通过解析user token或者device token来获取用户、设备信息,如果是合法的用户、设备,那么允许用户的后续操作,如果是不合法的,将请求直接拦截掉。
4) 总结
a) 流程
在app第一次打开时进行设备注册流程,同时下发device token、RSA公钥;除非device token失效,否则不重新注册设备。
客户端请求时通过RSA公钥加签,如果参数需要加密也通过此公钥加密。
服务端收到请求以后先通过解析device token(如果已登录则解析user token),如果出错直接返回,否则进行下一步。
服务端通过RSA私钥对请求数据进行验签,如果失败直接返回;如果成果则进行下一步。
进行协议转换,将HTTP请求转换为Dubbo请求,调用业务系统的接口。
返回结果。
b) 安全性分析
如果想要通过分析代码、抓取请求破解此流程,则必须按照整套流程进行。因为APP进行了混淆打包,所以此过程难度较大。
如果用户密码泄露,在用户重新登录之前,此用户的数据存在风险,但其他用户不会受到影响。
用户在另外设备上登录以后,之前的登录状态时效,即当用户手机丢失时,只需要找一台设备登陆一次即可保证自己的信息安全。
4 AES、RSA混合加密的安全方案
参考博客《AES+RSA混合加密原理详解》
https://blog.csdn.net/u011339364/article/details/78120904