在微服务架构中使用JWT

本文展示了如何使用 JWT 进行跨服务认证/授权。

在单体架构中,所有子系统都在一个应用程序中:身份验证和授权、会话管理器、业务逻辑等。如果我们在谈论微服务架构,有些事情变得更加复杂。

微服务

在微服务架构中,通常,认证/授权是一个单独的服务。
在这里插入图片描述
要请求服务,您必须首先进行身份验证并获取访问令牌。 一个示例是 OAuth 2.0 客户端凭据流。 要获取令牌,您需要传递 client idclient secret。 在下面的示例中,凭据作为 Authorization Basic 标头传递:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

响应此请求,我们将获得一个访问令牌,可用于请求所需的服务。 但是我们如何在服务端验证这个令牌呢?

验证

最明显的方法是查询授权服务并自省令牌。 但是当我们的系统包含数百个服务时,这样的请求会给授权服务带来很大的负担,并且有时会成为瓶颈。 为避免这种情况,我们可以在服务中验证令牌。 为此,我们可以使用自包含令牌,其中包括 JWT(JSON Web 令牌)。

JWT是某种token,一般由headerbodysignature三部分组成。 该标准在 RFC7519 中有更详细的描述。
在这里插入图片描述
这里 header 指定:

  • typ – 一种令牌类型(我们正在考虑JWT)
  • alg – 签名算法(例如 HS256 – 带有 SHA-256 的 HMAC(标头 + 有效负载 + 密钥))
  • kid – 当有多个密钥时使用,您需要了解哪个密钥是已签名的令牌

body 由标准标记和自定义标记组成:

  • iss – 发行人(发行令牌的人)
  • aud – 观众(令牌的对象)
  • iat – 合适签发
  • exp – 过期时间
  • sub – 主题
  • jti – JWT唯一ID

对称(例如,HS256)和非对称(例如,RS256)签名算法都可以用作签名。 在对称算法中,只有一个私钥,用于签名和验证。 在 非对称算法 中,签名者使用只有他自己保留的私钥,并验证 可以使用可以分发给每个人的公钥来完成签名。

事实证明,这样的签名令牌是自给自足的,不能在接收方不注意的情况下被操纵。 接收方收到token后,可以验证签名,确保token没有被修改。 这意味着令牌内的所有数据都可以信任。 这消除了通过调用远程服务来内省令牌的需要,从而减少了服务的负载。
在这里插入图片描述
如果服务 B 收到一个包含访问令牌的请求,它需要一个密钥来验证签名。 如果我们有一个包含几十上百个微服务的复杂系统,那么必要的服务就需要分发这个密钥。 如果使用对称算法,密钥很可能会泄漏。 拥有私钥,您可以签署任何内容的令牌。 因此,强烈建议使用非对称算法。 在这种情况下,通常会在身份验证/授权服务上放置一个端点,该服务返回一个用于检查签名的公钥列表。 必要的服务需要自己查询和缓存这些键。 使用这种方法,很容易组织密钥轮换——在某个时刻,auth-service 可以生成一组新的密钥并开始使用它。 接收到这样一个新令牌后,接收服务无法自行找到缓存的密钥,将再次请求 auth-service 并接收更新的密钥列表。

例子:

curl https://www.googleapis.com/oauth2/v3/certs

{
  "keys": [
    {
      "alg": "RS256",
      "kty": "RSA",
      "use": "sig",
      "n": "4DauU23AEpgBg3zJbqT8Fn-Zf817ru1moUjG75yJ-T0NpuQiggrXPn2YoKgo_qtnYloZh-RLjFfRv_Jb47riZhV5vsW7PiMR4MjlXgMWQlWG7kD9cIH5cTzBuEAzCkZZDu7XFkTfWUtRdWS5iKBjfQ465Qi5yFqfh7iHbQoKiN32pkWDI4MG8CUQC-YDbz77IRMpD39ZzNxkxYqbeJ226MrgKVGHFbmZLZPX8VX4r45NZifkPHa5-G5YDxaL622fkTqgPkyJtFOMy08X6K4BtVV0ZUJqi19bzEW970aI13seu0BzBsIspZ2NSPtljQqQFJTcW1EAmOCB5iNDi3J0mQ",
      "e": "AQAB",
      "kid": "fda1066453dc9dc3dd933a41ea57da3ef242b0f7"
    },
    {
      "e": "AQAB",
      "alg": "RS256",
      "n": "yJdNun_DT8_krjOUFMk4UPb7KgOyoN2EIHVL77LFLUlzFwOLon1pEceYcWffNQnjdtzDCN5-q6DxlIiJyDgQhPPMpJzMcpZceo0tKd-Ve1RLEUVcbnbjyZ-inrxVWfYTOuWTsutt7EylFDIMfw1Dh14IccFG5loyLdtZX2yejhXmJzMCxTISE_lCxCIiIqu5filfc3AnnyNb66Mv_oyK5z22pc9f-dFAmT3e5IXA-0UkrEVtLl7lRGmWdBkAkEWzhh17aQ0BynxpcTX5efGyr2b5ktUObCNdKMwNE4_Berz4l7_Oz6-gWDlyjbROrHKx0B27SFHdtNHbYARJsfVsjw",
      "kty": "RSA",
      "kid": "1727b6b49402b9cf95be4e8fd38aa7e7c11644b1",
      "use": "sig"
    }
  ]
}

验证令牌签名后,我们必须检查 issaud 声明。 iss 必须包含身份验证服务的标识符或 URL。 aud 包含为其生成令牌的服务的标识符或标识符列表。 我们必须确保我们的系统出现在这个列表中,并且这个令牌是为我们准备的。

在所有检查之后,我们可以授权请求并执行必要的操作。

令牌撤销

重要的是要注意,自包含令牌的问题之一是无法简单地撤销它们。 例如,一个令牌被泄露,我们需要禁止它在我们系统中的所有服务上使用。 但我们的服务只进行签名验证——令牌仍然有效。

在这种情况下,我们会缩短令牌的生命周期(分钟),之后它将不再有效。 但是如果这还不够,并且需要立即撤销令牌,您可以使用令牌黑名单作为键值存储库,它将存储被撤销令牌的 ID (jti)。 然后每个服务都应该查询该存储并确保令牌不在列表中。 没有必要将令牌永久存储在此存储库中,而只是在我们达到令牌中的 exp 值之前。 此外,令牌撤销事件本身非常少见,因此存储空间会很小且会被读取。

敏感的信息

应该理解,令牌的内容没有加密,令牌的正文是可以读取的。 如果您必须将令牌提供给第三方服务,那么您应该小心放入令牌正文中的数据。 但如果需要该数据,您可以使用加密版本的令牌 (JWE)。 另一种方法是不给出完整的令牌,而只给出它的标识符(jti),将jti——完整的令牌映射存储在存储库/缓存中。 在下一个请求中,将 jti 交换为完整令牌并在系统内使用完整令牌。

总结

在跨服务身份验证/授权实现中正确使用 JWT 可以让我们构建灵活、安全、高负载的应用程序。 了解它的工作原理将减少在构建系统架构时引入漏洞的可能性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱游泳的老白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值