ASP.NET Core JWT、Policy与权限边界如何具体落地实现?

摘要:这篇文章不讨论完整身份平台建设,只聚焦 ASP.NET Core 里最常见、也最容易出错的一段:JWT 认证、Policy 授权,以及资源级权限边界该怎么落到代码里。 问题背景 真实现场:一个后台退款接口原本只允许财务角色调用,但线上排查发
这篇文章不讨论完整身份平台建设,只聚焦 ASP.NET Core 里最常见、也最容易出错的一段:JWT 认证、Policy 授权,以及资源级权限边界该怎么落到代码里。 问题背景 真实现场:一个后台退款接口原本只允许财务角色调用,但线上排查发现,普通运营账号只要拿到有效 token,也能调用成功。 根因并不复杂: 接口加了 [Authorize] 系统只校验“是否登录” 没有继续校验角色、权限和资源归属 结果就是,认证做了,授权却只做了一半。 这也是很多系统的共性问题。认证只是在回答“你是谁”,授权回答的是“你能做什么”。如果这两件事没有拆开设计,接口表面安全,实际边界会很模糊。 原理解析 认证解决身份确认 认证的目标,是确认当前请求对应的是哪个用户、哪个客户端,常见做法就是校验 JWT 的签名、过期时间、签发方和受众。 这一步做完后,系统拿到的是一个 ClaimsPrincipal。它说明“请求身份可信”,但并不说明这个身份就有所有权限。 授权解决操作范围 授权是在认证之后,对用户能力做进一步判断。 在 ASP.NET Core 里,最常见的落点是 Policy。你可以按角色、权限声明、租户、部门或业务规则定义策略,而不是在控制器里到处手写 if 判断。 角色不等于权限模型 很多系统一开始只有 Admin、Operator、User 这几个角色,后来业务一复杂,就会发现角色粒度太粗。 更稳妥的方式通常是: 角色用于粗粒度分组 权限声明用于精细操作控制 例如“财务”和“运营”都属于后台用户,但是否允许退款、导出、调价,应该由权限声明决定,而不是只靠角色名硬编码。 资源级授权才是真正的边界 就算用户具备 orders.refund 权限,也不代表他可以操作所有订单。 很多越权问题出在这里:接口只校验了功能权限,没有校验资源归属,比如租户是否匹配、门店是否匹配、是否只能操作自己负责的数据。 所以完整授权通常分两层: 功能级:你有没有这个动作权限 资源级:你能不能对这条具体数据执行这个动作 示例代码 下面用一个“订单退款接口”来说明一套常见落地方式。
阅读全文