【后端】微服务后端鉴权方案

如何对 Helper Service 进行鉴权,核心原则是:Helper Service 不应该直接信任来自前端的请求,所有请求都必须经过 Core API 的授权和转发。


一、核心原则:信任链的建立

在架构设计中,我们必须明确"信任边界"。

  • 前端:运行在用户浏览器中,是完全不可信的环境。任何来自前端的 Token、密钥都可能被窃取或篡改。
  • Core API:是系统的"大脑",是唯一验证用户身份(登录、颁发 Token)和权限的地方。它运行在受控的服务器环境中,是可信的。
  • Helper Service :是系统的"手脚",只负责执行特定任务。它本身不认识用户,只应该信任来自 Core API 的指令。它也是运行在受控的服务器环境中,是可信的。
    因此,正确的信任链应该是:
    用户 -> 前端 -> Core API -> Helper Service
    前端拿着用户凭证(如 JWT)请求 Core API,Core API 验证通过后,以自己的身份去请求 Helper Service。Helper Service 只需要验证请求是不是来自 Core API 即可,无需关心最终用户是谁。

二、主流鉴权方案对比

基于上述原则,有几种主流的实现方案,各有优劣。

方案一:共享密钥 (Shared Secret / API Key)

这是最简单、最直接的方案,适用于大多数中小型项目或内部服务。
工作流程:

  1. 准备阶段 :生成一个高强度、足够长的随机字符串作为共享密钥(例如 A1b2C3d4E5f6G7h8...)。将这个密钥安全地配置在 Core API 和 Helper Service 的环境变量中。

  2. 请求阶段

    • 前端带着用户的 JWT 请求 Core API 的某个接口(例如 /api/v1/generate-report)。

    • Core API 验证 JWT 有效,确认用户有权限生成报告。

    • Core API 向 Helper Service 发送请求(例如 POST /helper/reports)。在请求头中加入共享密钥:

      复制代码
      POST /helper/reports
      Host: helper-service.example.com
      X-API-Key: A1b2C3d4E5f6G7h8...
      Content-Type: application/json
      { "userId": "123", "template": "monthly" }
  3. 鉴权阶段

    • Helper Service 收到请求后,从请求头 X-API-Key 中取出密钥。
    • 将其与自己环境变量中存储的密钥进行比对。
    • 如果一致,则请求合法,执行任务;否则,返回 401 Unauthorized403 Forbidden
      优点:
  • 实现简单:开发和理解成本极低。
  • 性能高:只是一个简单的字符串比对,几乎没有计算开销。
  • 无状态 :Helper Service 无需维护任何会话状态。
    缺点:
  • 密钥管理复杂
    • 轮换困难:如果密钥泄露,需要同时更新 Core API 和所有 Helper Service 的配置并重启,服务会有中断。
    • 权限粒度粗:所有使用同一个密钥的 Core API 都拥有对 Helper Service 的全部权限。无法做到"Core API 的 A 模块只能调用 Helper Service 的 X 接口"。
  • 安全性依赖 :安全性完全依赖于密钥的保密性。一旦 Core API 服务器被入侵,密钥就可能泄露。
    适用场景
  • 服务数量少(1个 Core API + 少量 Helper Service)。
  • 团队规模小,内部系统。
  • 对权限控制要求不高的场景。

方案二:JWT 签名 (Internal JWT)

这个方案更优雅、更安全,是微服务间通信的推荐方案之一。
工作流程:

  1. 准备阶段
    • Core API 生成一对非对称密钥(如 RSA):一个私钥和一个公钥。
    • Core API 保管好私钥(绝对不能泄露)。
    • Helper Service 保管好公钥(可以公开)。
  2. 请求阶段
    • 前端带着用户的 JWT 请求 Core API。

    • Core API 验证用户 JWT 有效后,自己生成一个新的、用于内部服务的 JWT(我们称之为 Internal JWT)。

    • 这个 Internal JWT 的 Payload 可以包含一些必要的信息,比如:

      json 复制代码
      {
        "iss": "core-api", // 签发者:Core API
        "sub": "helper-service-call", // 主题:表明这是一个内部调用
        "aud": "helper-service", // 接收者:Helper Service
        "exp": 1678886400, // 过期时间(可以设置得很短,比如5分钟)
        "permissions": ["report:generate"], // 精细的权限声明
        "user_context": { "userId": "123" } // 可选:传递一些用户上下文
      }
    • Core API 使用自己的私钥对这个 Internal JWT 进行签名。

    • Core API 将这个签好名的 Internal JWT 放在请求头(如 Authorization: Bearer <internal-jwt>)中,发送给 Helper Service。

  3. 鉴权阶段
    • Helper Service 收到请求后,取出 Internal JWT。
    • 使用自己存储的公钥来验证 JWT 的签名。
    • 验证 JWT 的其他声明:iss 是否是 core-apiaud 是否是自己?exp 是否过期?
    • 所有验证通过后,解析出 permissions,判断是否有权限调用当前接口,然后执行任务。
      优点:
  • 安全性高:基于非对称加密,Helper Service 即使被入侵,也无法伪造请求给其他服务(因为它没有私钥)。
  • 无状态且可扩展:Helper Service 无需存储任何状态,只需通过公钥验证签名即可。可以轻松地增加新的 Helper Service,只需给它公钥即可。
  • 信息丰富且精细:可以在 JWT Payload 中携带丰富的上下文信息和权限声明,实现非常精细的权限控制(例如,A 服务只能读,B 服务能读写)。
  • 易于轮换 :需要轮换密钥时,Core API 生成新的密钥对,然后逐步将 Helper Service 的公钥更新为新的即可,可以实现平滑过渡。
    缺点:
  • 实现稍复杂:需要理解 JWT 和非对称加密的原理,代码实现上比共享密钥复杂一点。
  • 有轻微性能开销 :JWT 的签名和验证比字符串比对要消耗更多的 CPU 资源,但对于现代服务器来说,这点开销通常可以忽略不计。
    适用场景
  • 微服务架构,服务数量较多。
  • 对安全性和权限控制有较高要求的系统。
  • 需要未来扩展的系统。

方案三:服务网格 (Service Mesh, e.g., Istio, Linkerd)

这是一种更高级、更底层的方案,将鉴权等基础设施功能从业务代码中剥离出来。
工作流程:

  1. 部署阶段:在 Kubernetes 或其他容器编排平台中部署一个服务网格。每个服务(Core API, Helper Service)的旁边都会被注入一个轻量级的代理(称为 Sidecar,如 Envoy)。
  2. 配置阶段 :通过服务网格的控制平面(如 Istio)配置策略,例如:
    • "允许来自 core-api 服务的请求访问 helper-service 服务的 /reports 接口。"
    • "拒绝所有其他来源的请求。"
  3. 请求阶段
    • Core API 的业务代码像往常一样,直接向 helper-service 的域名发送请求(不需要在代码里加任何鉴权逻辑!)。
    • 这个请求实际上被 Core API 旁边的 Sidecar 拦截了。
    • Sidecar 根据预设的策略,自动为请求加上身份证明(比如 mTLS 证书),然后转发给 Helper Service 的 Sidecar。
  4. 鉴权阶段
    • Helper Service 的 Sidecar 拦截到请求。
    • 它验证请求来源的身份证书是否符合控制平面下发的策略。
    • 如果验证通过,才将请求转发给 Helper Service 的业务代码。否则,直接拒绝。
      优点:
  • 业务代码无侵入:开发人员完全不需要关心服务间的鉴权逻辑,可以专注于业务实现。
  • 统一管理和策略执行:所有服务的通信策略都在一个地方集中管理,非常清晰,不易出错。
  • 功能强大 :除了鉴权,服务网格还能提供流量管理、熔断、遥测、链路追踪等强大功能。
    缺点:
  • 架构复杂:引入服务网格会极大地增加整个系统的复杂度,对运维和开发人员的要求很高。
  • 资源开销:每个服务旁边都运行一个 Sidecar 代理,会带来额外的 CPU 和内存消耗。
  • 学习曲线陡峭 :需要投入大量时间学习和维护。
    适用场景
  • 大型、复杂的微服务集群。
  • 拥有专业运维团队的公司。
  • 对系统可观测性、安全性和流量控制有极致要求的场景。

三、最佳实践与建议

结合以上分析,我为你提供一个从易到难的演进建议:

阶段一:项目初期,快速验证 (推荐方案一)
  • 选择共享密钥
  • 做法
    1. 使用 secretsmanagervault 等工具来管理共享密钥,而不是硬编码在代码或 .env 文件里。
    2. 在 Core API 和 Helper Service 中,实现一个中间件,统一处理 X-API-Key 的验证。
    3. 确保所有 Helper Service 的接口都部署在内网,不对外暴露,增加一道物理屏障。
  • 理由:在项目初期,快速迭代是第一要务。共享密钥方案简单高效,能让你的团队快速把功能跑起来,同时基本的安全性也能得到保障。
阶段二:业务发展,服务增多 (推荐方案二)
  • 选择JWT 签名
  • 触发时机:当 Helper Service 的数量超过 3-5 个,或者你发现需要为不同的 Helper Service 配置不同的调用权限时。
  • 做法
    1. 设计一个标准的 Internal JWT 结构,包含 iss, aud, exp, permissions 等关键字段。
    2. 在 Core API 中封装一个 callHelperService 的工具函数或库,内部自动完成 Internal JWT 的生成和请求发送。
    3. 在 Helper Service 中,使用成熟的 JWT 库(如 jsonwebtoken for Node.js, PyJWT for Python)来验证签名和解析权限。
    4. 同样,私钥和公钥的管理要使用专业的密钥管理工具。
  • 理由:这个方案在安全性和可维护性上取得了很好的平衡。它为系统的长期发展奠定了坚实的基础,是微服务架构的"甜点区"方案。
阶段三:规模庞大,体系成熟 (考虑方案三)
  • 选择服务网格
  • 触发时机:当你的微服务数量达到几十甚至上百个,团队规模扩大,手动管理服务间通信和策略变得非常痛苦时。
  • 做法
    1. 评估并选择一个服务网格产品(Istio 是最流行的选择)。
    2. 组建或培养一支具备 SRE(网站可靠性工程)能力的运维团队。
    3. 逐步将服务迁移到服务网格中,先从非核心业务开始,逐步推广。
  • 理由:此时,架构的复杂性已经超出了人工管理的范畴。服务网格通过将基础设施能力下沉,能够统一、自动化地解决这些问题,提升整个系统的稳定性和效率。

总结

方案 实现复杂度 安全性 权限粒度 可扩展性 推荐场景
共享密钥 项目初期、少量内部服务
JWT 签名 微服务架构、主流选择
服务网格 极高 极细 极高 大规模复杂系统
对于绝大多数项目来说,从共享密钥开始,并在适当时机迁移到 JWT 签名方案,是最务实和明智的选择。它能很好地平衡开发效率、系统安全和未来的可扩展性。
相关推荐
MrSYJ28 分钟前
会了就涨工资的技巧:oauth2.0资源服务器配置
spring cloud·微服务·架构
子兮曰32 分钟前
纯Bun微应用架构:零依赖构建全栈多包系统
架构
玦尘、3 小时前
微服务相关面试题
微服务·云原生·架构
叫我阿柒啊3 小时前
Java全栈工程师的实战面试:从基础到微服务的全面解析
java·数据库·vue.js·spring boot·微服务·前端开发·全栈开发
MrSYJ4 小时前
nimbus-jose-jwt你都会吗?
java·后端·微服务
kakaZhou7194 小时前
apisix硬核介绍
后端·架构
猿java5 小时前
分布式和微服务,它们有什么区别?该如何选择?
分布式·微服务·架构
༒࿈༙྇洞察༙༙྇྇࿈༒5 小时前
jwt原理及Java中实现
java·开发语言·状态模式·jwt