返回专题首页

Node.js 专题

身份认证与权限控制:Session、JWT、RBAC 与常见安全问题

只要服务端项目对外开放,认证与权限几乎就绕不过去。真正难的地方从来不是会不会签发 token,而是知道用户身份、会话状态、权限判定和安全边界分别应该落在哪一层。这一篇会把认证与授权主线系统串起来。

Node.js 专题第 31 篇 / 40 篇4 分钟

只要服务端项目对外开放,认证与权限几乎就绕不过去。真正难的地方从来不是会不会签发 token,而是知道用户身份、会话状态、权限判定和安全边界分别应该落在哪一层。这一篇会把认证与授权主线系统串起来。

认证和授权为什么不是一回事?

很多人会把“登录”和“权限”混着说,但它们解决的是不同问题:

  • 认证回答的是“你是谁”;
  • 授权回答的是“你能做什么”。

这两者高度相关,但不能混成同一个概念。只要边界不清楚,系统后面就很容易出现:

  • 登录了却还能越权;
  • 角色判断和身份判断写在一起;
  • 某些接口到底是没登录,还是没权限,表达不清。

SessionJWT 到底怎么选?

它们本质上都是在解决“如何让后续请求持续携带身份状态”这个问题,只是策略不同。

Session 更强调服务端保存会话状态,因此更容易集中控制、失效和管理;JWT 更强调令牌自描述与跨服务携带能力,因此在某些分布式和前后端分离场景里会更灵活。

真正的选型重点不是跟风,而是看你的系统更在意什么:

  • 更强的服务端控制力;
  • 还是更灵活的无状态协作;
  • 更简单的失效管理;
  • 还是更方便的跨边界传递。

也就是说,它们的差异不在于“谁更先进”,而在于状态由谁掌握、失效如何传播、系统边界如何协作。只要这几点想不清楚,选型讨论就很容易停留在表面印象。

权限模型到底该怎么理解?

权限控制真正难的地方,不是写一个 if 判断,而是定义边界。最常见的模型之一是 RBAC,也就是基于角色的权限控制。它的优势在于简单、直观、适合很多后台系统。

但随着业务复杂度上升,你很快会发现,仅仅有角色还不够。因为现实问题经常变成:

  • 这个人虽然是某个角色,但只能操作自己范围内的数据;
  • 某个动作并不是看角色,而是看资源归属和状态;
  • 权限需要细到字段、操作或条件层级。

这说明权限设计本质上是业务边界设计,而不是单一中间件问题。

更成熟的权限设计,往往会把几个层次拆开看:

  • 是否已登录;
  • 是否具备某类角色或能力;
  • 是否对当前资源拥有操作范围;
  • 当前资源状态是否允许执行该动作。

当这些层次被混成一个 if 判断时,系统一开始可能还能跑,但很快就会在边界条件下变得难以解释。

常见安全问题为什么总是反复出现?

因为很多安全问题看起来像低级错误,但本质上都和边界意识不足有关。比如:

  • 只验证身份,不验证资源归属;
  • token 泄漏后没有合理失效策略;
  • 敏感接口缺少额外保护;
  • 错误信息暴露太多内部细节;
  • 默认把客户端传来的身份信息当真。

所以,安全很多时候不是“高级专题”,而是日常接口设计是否认真。

除此之外,认证与权限还有几个很容易被忽略的操作性问题:

  • 登出或权限变更后,旧状态多久失效;
  • 多端登录时,会话是否需要互斥或可管理;
  • 敏感操作是否需要二次确认或更短期的凭证;
  • 权限拒绝时,错误响应是否既明确又不过度暴露内部信息。

这些都不是锦上添花,而是系统进入真实使用后迟早会遇到的问题。

一个更稳妥的认证与授权链路应该长什么样?

如果把这件事收拢成一条最小闭环,通常至少要做到:

  • 先清楚区分认证和授权;
  • 选定 SessionJWT 时明确状态管理策略;
  • 把角色判断和资源级权限判断区分开;
  • 对敏感接口补足失效、续期和异常处理策略;
  • 默认把客户端输入当作不可信,再逐层验证。

总结

这一篇我们把身份认证和权限控制从“登录功能”提升成了一整条系统设计主线:认证负责确认身份,授权负责限制能力,SessionJWT 只是两类不同实现策略,而真正的风险则往往藏在资源边界和安全细节里。

下一篇我们会进入服务端另一个很常见、也很容易留下隐患的能力,也就是文件上传与静态资源处理。