在传统的单体架构中,单个服务保存所有的用户数据,可以校验用户,并在认证成功后创建HTTP会话。在微服务架构中,用户是在和服务集合交互,每个服务都有可能需要知道请求的用户是谁。一种朴素的解决方案是在微服务系统中应用与单体系统中相同的模式,但是问题就在于如何让所有的服务访问用户的数据。解决这个问题大致两个思路:若使用共享用户数据库时,更新数据库表会成为一个难题,因为所有服务必须同时升级以便能够对接修改后的表结构;若将相同的数据分发给所有服务时,当某个用户已经被认证,如何让每个服务知晓这个状态是一个问题。
1.单点登录(SSO)
这种方案意味着每个面向用户的服务都必须与认证服务交互,这会产生大量非常琐碎的网络流量和重复的工作,当动辄数十个微应用时,这种方案的弊端会更加明显。 在其他方面,选择SSO方案安全性会很好,用户登录状态是不透明的,可防止攻击者从状态中推断任何有用的信息。
2. 分布式 Session 方案
分布式会话方案,原理主要是将关于用户认证的信息存储在共享存储中,且通常由用户会话作为key来实现的简单分布式哈希映射。 当用户访问微服务时,用户数据可以从共享存储中获取。 该解决方案的另一个优点是用户登录状态是不透明的。 当使用分布式数据库时,它也是一个高度可用且可扩展的解决方案。 这种方案的缺点在于共享存储需要一定保护机制,因此需要通过安全链接来访问,这时解决方案的实现就通常具有相当高的复杂性了。
3.客户端令牌方案
此令牌在客户端生成,由身份验证服务进行签名,并且必须包含足够的信息,以便可以在所有微服务中建立用户身份。 令牌会附加到每个请求上,为微服务提供用户身份验证。 这种解决方案的安全性相对较好,但身份验证注销是一个大问题, 缓解这种情况的方法可以使用短期令牌和频繁检查认证服务等。对于客户端令牌的编码方案,推荐使用 JSON Web Tokens(JWT),它足够简单且库支持程度也比较好。
4.客户端 Token 与 API 网关结合
客户端令牌与API网关结合,这个方案意味着所有请求都通过网关,从而有效地隐藏了微服务。 在请求时,网关将原始用户令牌转换为内部会话ID令牌。 在这种情况下,注销就不是问题,因为网关可以在注销时撤销用户的令牌。 这种方案虽然库支持程度比较好,但实现起来还是可能很复杂。
建议使用客户端令牌(使用JWT)和API网关结合的方案,因为这个方案通常使用起来比较容易,且性能也不错。 SSO方案虽然能满足需求,但他认为还是应该避免使用。若分布式会话方案所需要的相关技术已经应用在你的场景上,那么这个方案也是比较有趣的。他同时强调在选择解决方案时应着重考虑注销的重要性。