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

如何对 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 签名方案,是最务实和明智的选择。它能很好地平衡开发效率、系统安全和未来的可扩展性。
相关推荐
NAGNIP18 小时前
Serverless 架构下的大模型框架落地实践
算法·架构
brzhang18 小时前
为什么说低代码谎言的破灭,是AI原生开发的起点?
前端·后端·架构
kfyty72519 小时前
loveqq-bootstrap 和 springcloud-bootstrap 有什么区别
后端·架构
brzhang19 小时前
干翻 Docker?WebAssembly 3.0 的野心,远不止浏览器,来一起看看吧
前端·后端·架构
数据智能老司机1 天前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
IT小番茄1 天前
Docker容器间互联的Zabbix监控项目知识整理[十一]
架构
小刘大王1 天前
while循环与死循环
架构·前端框架
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
未来影子1 天前
SpringAI(GA):MCP Server 服务鉴权(过滤器版)
架构