余楠,就职于ThoughtWorks。
本文已投稿至ThoughtWorks洞见:https://insights.thoughtworks.cn/service-mesh-authentication-authorization/
认证与授权几乎是所有系统必不可少要处理的问题。在传统架构下,我们习惯了在程序中写一些代码或引一些类库来处理其相关的逻辑,但如果在Service Mesh架构下,会有什么不同?
Service Mesh的核心是将一切非业务功能交给基础设施层,讨论Service Mesh架构下的认证与授权,实质上是讨论能否将认证与授权的处理逻辑委托给基础设施层,从而让应用层更加专注于业务。
为了能让我们的讨论更加形象化,我们可以借助一个案例。
背景
某组织要举办一次篮球相关的活动,要求参与者以团体的方式报名。
参赛人员通过微信账号登录系统并验证手机号后,填写个人信息及团队其它成员的手机号与信息并报名活动,其它成员通过微信账号登录并验证手机号后,可以看到与自己关联的活动申请。
组织方工作人员同样通过微信登录并验证手机号,之后可以获取其所负责区域的活动申请并做审核操作。
系统结构
我们需要一个用户池来管理用户,但我们不想在用户的账号密码等逻辑上花费太多精力,因此决定使用微信作为我们的用户池,用户进入我们的系统时,使用微信登录即可,这样就可以将“创建新用户”,“修改密码”,“找回密码”和“短信验证码”等逻辑委托给微信,同时也免去了用户进入系统时的“注册”过程,一举两得。
接下来,我们需要一个维护活动申请的服务,但因为活动申请需要的信息较多,用户可能无法一口气完成,所以还需要一个能帮助用户分步骤创建活动申请的服务。
最后,活动组织方需要审核活动申请,但活动组织方的管理人员也是用微信登录系统,那么我们的系统就得有能力知道当前用户是不是工作人员,因此我们需要一个领域专属用户信息服务。
以下是一幅简单的系统逻辑结构图:
此图隐去了其它相关部分,以免结构图变得复杂,导致讨论范围扩散。
- User Pool:用户池。可以是微信或Google,可以是OKTA或Idaptive,可以用AWS Cognito搭建,也可以完全自己构建,实际上大家的手机号也是一个用户池。
- Applicant Service:帮助用户分段构建申请信息的服务,通过它我们还可以提供通过社交网站导入成员信息的方式。
- Application Service:负责活动申请的服务。
- Domain Specific User Information Service:User Pool只能提供有限的信息,此场景中使用的微信用户池能提供的信息更有限,由于这个系统需要记录专有的信息,所以需要这样的服务。
认证
在这个案例中,有两类具有不同数据访问权限的用户,为了能知道当前访问系统的用户归属于哪一类,那么系统必须得先知道当前用户是谁,这样才能找出用户所具有的授权。认证是授权的先决条件,识别出当前用户是谁,就是“认证”。认证大体上分为“系统识别用户”和系统内“微服务识别用户”,“微服务识别用户”是我们的重点。
系统识别用户
最终处理用户请求的是系统中的各个微服务,但我们不能让每个微服务都和登录流程打交道,所以需要在系统将请求转发给微服务前,完成对用户的识别。
登录
用户要使用我们的系统,首先要去登录界面,登录界面引导用户使用微信登录。微信做为User Pool帮我们完成对用户的认证,然后我们的系统会给用户发一个“外部通行证”,通常情况下,“外部通行证”是一个随机字符串,放在Cookie中。之后用户向系统发起请求时,便不再需要提供账号密码,只要带上个这个“外部通行证”即可。
访问系统
用户向系统发起请求时,需要在请求中附上“外部通行证”,这样系统就能知道请求来自于谁。如果请求中没有“外部通行证”或“外部通行证”不合法,则要求用户重新登录。如果“外部通行证”合法,系统则通过“外部通行证”,找到用户在系统中的记录及其关联的授权信息,然后生成一个“内部通行证”。“内部通行证”通常情况下是JWT,包含更加丰富的信息,并对用户不可见,系统内微服务只关心“内部通行证”。
微服务识别用户
用户的请求最终需要访问系统内的微服务,为确保到达微服务的请求是由合法的用户发起的,而不是系统内被劫持的其它子系统捏造的,微服务仍然需要验证请求者的身份。微服务验证请求者身份的方式,就是验证请求中附带的“内部通行证”是否合法。
在被请求的微服务处理请求前,需要先验证“内部通行证”的合法性,也就是验证请求中附带的JWT。验证JWT需要先获取Key(加密方式决定Key的类型),之后用这个Key计算JWT是否被篡改过。案例使用的JWT,但验证内部通行证的主流方式都不涉及被请求服务的业务。这个过程与微服务自身承载的业务完全没有关系,但它又是微服务不可或缺的一部分。传统方式下,我们会把这部分逻辑放在微服务中,并让这部分逻辑独立于微服务的业务模块,但微服务的职责范围还是扩大了,导致构建微服务时,开发人员的关注范围要扩大。如果把这部分逻辑交给Sidecar,我们的微服务可以更加纯净,开发人员在构建微服务时也可以更加聚焦。
授权
当请求者的身份被识别后,下一步要做的就是检查请求者是否被授权访问其请求的内容。
用户到服务
用户需要向Application Service提交活动申请,那么用户就得有对Application Service的访问权限。
用户的请求到达后,首先检查“内部通行证”,确保请求是由合法的用户发起的。之后检查发起请求的用户是否有权限访问这个服务或这个服务的某一个API,有多种方式可以做,案例中的方式为检查“内部通行证”中对这个API访问权的描述。这个过程与微服务自身承载的业务完全没有关系,与前面“认证”中“微服务识别用户”的情况相同。
服务到服务
用户发起的请求,可能系统内一个微服务就可以完全处理,也有可能需要多个微服务协同处理。所以对于微服务,请求者可以是用户,也可以是其它微服务。在我们这个系统中,微服务之间会有交互,但它们之间的访问性不是无限制的。Application Service在接到用户提交的活动申请时,需要访问Domain Specific User Information Service获取一些数据,用来构建活动申请。对于Domain Specific User Information Service,来自Application Service的请求,应予以处理,但如果请求来自其它服务,则要拒绝。
服务间的访问性配置需由另外的服务提供,做这步检查时需要先获取服务间的访问性配置,然后依据配置拒绝或放行请求。这个过程与微服务自身承载的业务完全没有关系,同样与前面“认证”中“微服务识别用户”的情况相同。
用户到服务的特定数据
从参赛人员的角度看“Application Service中的数据”
一个活动申请包含多位参赛人员及他们的手机号与身份证号,当团队中一位成员代表团队提交了活动申请,我们需要让团队其他成员在完成认证后,也能看到这份活动申请及其状态。这种情况下,活动申请是否可被访问的“配置”包含在活动申请自身中,其它服务无法提供。也就是说,只有Application Service自身才能提供活动申请能否被访问的“配置”。
如果将授权检查的逻辑提成Sidecar
这个Sidecar在做数据访问性检查时,要请求微服务来获取数据,用于判断请求是否被授权。这个提供信息用于做访问检查的API是被做“数据访问性检查”的Sidecar“驱动”出来的,那么本质上,微服务参与了权限验证。
因为负责数据授权检查的Sidecar需要从微服务获取数据才能完成工作,所以微服务需要对做数据授权检查的Sidecar提供可获取授权信息的API,这导致授权检查的职责没能被完全委托出去。
如果不将授权检查的逻辑提成Sidecar
对于很多使用关系型数据库的微服务来说,可能只是SQL中INNER JOIN的过滤,无需额外做任何事情。如果需要过滤从数据库中拿到的数据,服务内数据通信的便捷性强于服务间数据通信。
刚才我们提到,只有Application Service自身才能提供活动申请能否被访问的“配置”,这其实已经说明了对于这种场景,维护这份“配置”,就是Application Service的业务。因此,这种场景让微服务自己做,能让逻辑更加内聚,也能减少不必要的工作。
从“活动申请”审核人员的角度看“Application Service中的数据”
由于活动在10个城市中举办,现有5个活动管理员,平均每人管理两个城市的活动申请,不同管理员之间不共享数据。
我们有城市1 - 10,其中1和2归属管理员1,当管理员1要获取他所负责的所有活动申请时,需要用到类似下面的接口。
GET /applications?cities=1,2
他可能也只想查看城市1最近几天收到的申请。
GET /applications?cities=1&submittedAfter=xxxx-xx-xx
但他不能查看城市1或2以外任何城市的申请,下面的请求应该被直接拒绝。
GET /applications?cities=3,6
将授权检查的逻辑提成Sidecar
因为数据的访问性配置是由其它服务提供的,微服务不需要也没有能力向这个Sidecar提供支持,也不需要知道有这个Sidecar。但负责数据授权检查的Sidecar需要学习微服务的业务,并且这部分内容无法复用。
虽然这个Sidecar要学习微服务的业务,成为这个服务专有的Sidecar,但是从微服务的角度看,数据授权检查的逻辑被完全委托出去了,微服务可以更加纯净,开发人员在构建微服务时也可以更加聚焦。
总结
在微服务处理请求前,对请求者身份的验证也就是对认证信息的检查,应委托给基础设施层。
授权配置在微服务之外时,如果是“服务到服务”和“用户到服务”的场景,应委托给基础设施层,如果是“用户到服务的特定数据”,类似按城市分配管理员审核活动申请的场景,可优先考虑委托给基础设施层。
授权配置在微服务之内时,如果是“用户到服务的特定数据”,类似参赛人员与活动申请的关系,属于微服务本身需要维护的业务逻辑,应优先考虑服务内实现。
网友评论