其他分享
首页 > 其他分享> > 分布式系统下的认证与授权[转]

分布式系统下的认证与授权[转]

作者:互联网

文章转载自 : https://www.bmpi.dev/dev/authentication-and-authorization-in-a-distributed-system/ 非原创

在软件系统设计中,如何让应用能够在各种环境中安全高效的访问是个复杂的问题,这个问题的背后是一系列软件设计时需要考虑的架构安全问题:架构安全性|凤凰架构

在漫长的架构演进历史中,业界对这些问题已经有很成熟的解决方案。在架构安全这块,最好的是遵循技术标准与最佳实践,尽可能不重复造轮子或“创新”。下面这个思维导图就是针对这些问题的常见的技术标准及方案:

img

在研究分布式系统的认证和授权问题前,让我们回到单体架构的时代,看看在单体架构上这些问题是如何被解决的。

单体系统

认证

认证主要解决 你是谁 的问题,从方式上来看有以下三种:认证|凤凰架构

在单体系统时代,认证方式一般是在通信信道上开启HTTPS,在通信协议上利用 HTTP Basic/Digest/Bearer/HOBA/OCRA 等方式并在通信内容上结合表单或 TOTP 等的认证组合方式。这样可以从通信的不同阶段获得相应的安全保证。

如果想对基于HTTP协议的认证方式做进一步的了解,可以参考这两篇文章:

  1. 认证|凤凰架构
  2. 细说API -认证、授权和凭证- Thoughtworks洞见

单点登录(SSO

认证的一个常见应用场景是单点登录。单点登录主要解决了一个一次登录访问多个独立应用的问题。在单点登录方案出现之前,每个应用都需要独立登录维持各自的会话。相关的技术方案已经很成熟,主要有以下:

授权

授权主要解决 你能做什么 的问题,从方案上来说有以下几种:

如果想对授权做进一步的了解,可以参考这篇文章:

  1. 授权|凤凰架构

凭证

凭证是为了解决在认证授权后如何承载认证授权信息的问题。在单体应用时代,主流的解决方案是基于HTTP协议的Cookie-Session机制为代表的服务端状态存储技术。

由于HTTP协议本身是无状态的,要维持一个会话(Session),而不是每次访问都重新认证授权,需要客户端也就是浏览器通过Cookie来存储服务器端返回的一个凭证信息,这个凭证信息一般是一串随机的字符串,用来代表用户此次的会话标识。每次请求浏览器都会在HTTP Header中携带这个Cookie信息,应用拿到这个会话标识后从内存或缓存(Cache)中查询出用户的信息,这样就定位到了具体的用户,实现了会话的维持。

这套古老的方案存在以下先天优势:凭证|凤凰架构

一切都很美好,直到我们来到了分布式系统时代。

分布式系统

分布式系统与单体系统的一大区别就是状态管理。分布式系统通过把单体系统中有状态的部分转移到中间件中去管理,从而很容易做到水平扩容,提高系统峰值处理能力。在架构认证和授权部分,分布式和单体并没有什么不同,唯独有变化的在持有状态的凭证部分。

我们知道单体应用在服务端管理用户会话信息,客户端只持有会话标识。如果服务端要将此用户会话状态转移出去有两种处理思路:

JWT

如果你对JWT不了解,可以先看这两篇:

  1. JWT |凤凰架构
  2. The Hard Parts of JWT Security Nobody Talks About

由于JWT的Payload并未做过多限制,所以很容易产生滥用的问题,并且带来很多误解。 比如下面的一些问题:

相信看了上述的一些问题,你对JWT的简单、安全有了新的理解。这还没完,JWT还有以下一些Cookie-Session没有的问题:

JWT解决了Cookie-Session方案在分布式系统中因CAP的限制而带来的问题,但同时也带来了一些新的问题。所以并不能说JWT就是Cookie-Session在分布式系统中的完美替代。

那么JWT的最佳使用场景到底是什么?这篇 Stop using JWT for sessions 给出了以下的结论:JWT更适合作分布式系统中的一次性令牌使用。分布式系统继续使用Cookie-Session做会话管理,但可以在认证鉴权后生成JWT做分布式系统内部服务调用间的一次性令牌。

让我们通过一个例子来理解下在分布式系统下的认证授权场景。

一个例子

img

  1. 此处Auth服务承担的是授权(Authorization)的职责,而不是认证(Authentication)的职责;
  2. OAuth2在协议中是做授权框架的,但是其一般需要登录授权,也能实现SSO的功能。
  1. 用户通过HTTPS访问我们的应用。当请求发送至微服务网关层(Gateway),网关检测HTTP Header中的Cookie发现没有 SESSIONID 这个键值对,重定向至SSO登录页面。
  2. 用户通过SSO登录我们的应用。
    1. 用户信息存放至AD/LDAP等系统中。管理员提前给用户配置好角色权限。
    2. SSO集成方案我们选择OIDC。OIDC集成了AD/LDAP,当用户提供正确的用户名和密码后,SSO重定向至网关。
    3. 网关生成了 SESSIONID 键值对并通过HTTPSet-Cookie响应给用户浏览器设置了此Cookie。
  3. 浏览器重新发起带SESSIONIDCookie的请求。网关经过查询其缓存或中间件(如将会话信息存放至Redis)中的Session信息确认了用户的身份信息。之后网关请求Auth服务利用其私钥签名生成JWT凭证,JWT Payload中可以存放一部分用户信息和角色信息,这些信息可以从中间件中或AD/LDAP中查询出。
  4. 网关之后将此JWT凭证通过反向代理转发至内部的BFF服务,之后请求到达内部的领域微服务。
  5. 各领域微服务接受到请求后,先从HTTP Header中拿出JWT凭证。
    1. 在执行真正的业务逻辑前,先利用之前定时从Auth服务中同步获取的公钥。
      1. Auth服务通过一个类似 https://<your_domain>/.well-known/jwks.json 的API提供JWT公钥的分发。关于 .well-known 前缀,可阅读 RFC 5785 做进一步了解。在 jwks.json 文件中,我们可以找到 JWK 或JSON Web Key,这是我们用来验证签名的公钥。
      2. 校验JWT这块逻辑属于微服务共有的部分,一般可以开发一个SDK包来做这个通用的工作。为了提高性能,可使用缓存技术,定时从Auth中同步公钥。
    2. 获取到公钥后验证成功后拿出JWT Payload即可获取到用户信息和角色权限。

全部流程就是这样,我们得到了以下的一些好处:

架构总是在演进,也许分布式系统中很多问题我们还没完全解决,就来到了云原生时代。

云原生系统

如果你对云原生应用开发还不了解的话,可以先看看我这篇 K8S云原生应用开发小记。云原生系统其实并不是什么后分布式系统时代。它们两者都是为了解决不同场景的问题而出现的解决方案。

在认证授权这块,云原生系统的优势在于可以通过 服务网格(Service Mesh) 做一些业务系统中通用的切面工作,比如我们在分布式系统中遇到的校验JWT的SDK其实就可以放入服务网格中的边车(Sidecar)去实现,让业务应用更专注特定领域的业务。

由于这篇文章并不主要讨论云原生,对这部分感兴趣的可以参考以下两篇文章做进一步了解:

  1. Service Mesh架构下的认证与授权
  2. 微服务下的身份认证和令牌管理

总结

由于篇幅及能力限制,这篇文章我只能从高层次梳理在不同架构演进中认证、授权及凭证这些和架构安全相关的技术的发展过程。由于这些技术涉及了大量的技术标准及实践,很难在一篇文章中对这些技术做详尽的分享,更无法去分享如何实现。但有了这些理论支持和最佳实践,希望能让你在实现的过程中多了一个指引。如果你想进一步了解,可参考文章中的参考文章链接。

最后,技术总是在不断的发展,但并不是新技术总比老技术“先进”。正如文章中对Cookie-Session与JWT的分析对比,技术方案总是充满了各种 Trade-off。而作为一个工程师,我们能做的就是认清这些技术的历史背景及局限性,选择最适合项目需求的技术方案。

标签:JWT,用户,认证,Session,Cookie,分布式系统,授权
来源: https://www.cnblogs.com/Benjious/p/16483817.html