dual-authentication-model

双重认证模型

admin 服务在每一个请求上都回答两个不同的信任问题,并且把它们干净地分开。一个问题是谁在调用——内部服务之间传输层面的边界。另一个问题是这次调用背后是哪个人——决定能做什么的身份。两者各有一层,谁也不替谁干活。

第一层:服务间 JWT

内部流量用一个手写的 JSON Web Token 来认证,就是经典的三段式——header、payload、signature——用一个共享服务密钥、以 HMAC-SHA256 签名。没有外部库来做签名;代码库自己基于 Node 内置的 crypto 铸造和校验这个 token。

token 是按每次对外调用当场铸造的。签名一侧盖上签发时间,可选地盖上过期时间,并带上一个标明调用方服务的 claim。校验一侧是一个跑在路由处理之前的请求钩子:它要求带 bearer token,重新计算签名并比对,检查 header 声明的算法和类型是否符合预期,并拒绝已过期、或签发时间落在未来的 token——每个边界都留一点余量,容忍机器之间的时钟漂移。校验通过后,它把一小段上下文(调用方服务名加上原始 claims)挂到请求上;任何失败都抛 401。健康检查这类公开路由整个跳过这套检查。

效果是:每一个非公开的入站请求,在处理器看到它之前,都必须先证明自己来自一个受信任的内部服务。

第二层:管理会话守卫

服务 JWT 对人是谁只字不提——那是第二层的事。人的身份搭载在一个从浏览器转发过来的会话 cookie 上(另有一个开发环境下的回退)。admin 服务自己并不校验这个会话,而是由一个 helper 从请求里取出会话 cookie,交给 auth 服务自己的身份端点去解析,解析出来的用户——id、email、一个 user-type、以及 name——才是 admin 所信任的东西。

而这次解析调用本身又被包在第一层的服务 JWT 里,于是两层叠合起来:发往身份端点的那个请求,同时带着机器凭据和人的会话 cookie。

解析之上坐着两个守卫。一个只要求解析出任意用户,会话缺失或无法解析时以 401 失败。另一个专门要求是管理员用户,当解析出的 user-type 是别的东西时以 403 失败。是不是管理员,纯粹由 auth 服务返回的 user-type 推导,绝不在本地自行计算。

为什么是这个形状

这个设计守住了一个身份的单一真相来源:auth 服务。admin 服务对会话保持无状态——它不持有会话存储,也不从密码出发做任何授权判断。它只问"这个 cookie 是谁?"和"这个人是管理员吗?"。机器信任的问题和人身份的问题保持互相独立,正是这一点让浏览器请求和服务间请求可以走同一条代码路径,却按不同的证据来裁决。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

dual-authentication-model