首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >ASP.NET Core 认证与授权体系:从小白到进阶,系统讲透 Scheme、Claim、Policy、Role 到底是什么

ASP.NET Core 认证与授权体系:从小白到进阶,系统讲透 Scheme、Claim、Policy、Role 到底是什么

原创
作者头像
步步为营DotNet
发布2026-07-09 17:59:23
发布2026-07-09 17:59:23
40
举报


一、痛点引入:为什么明明代码能跑,却还是看不懂?

很多初学者都会遇到这种情况:

你看到这样的代码:

代码语言:javascript
复制
 builder.Services.AddAuthentication("Bearer")
     .AddJwtBearer("Bearer", options => { ... });
 ​
 builder.Services.AddAuthorization(options =>
 {
     options.AddPolicy("AdminOnly", policy =>
     {
         policy.RequireRole("admin");
     });
 });
 ​
 app.MapGet("/admin", (ClaimsPrincipal user) => ...)
    .RequireAuthorization("AdminOnly");

代码能跑,但心里其实一堆问号:

  • "Bearer" 是什么?
  • 为什么叫 Scheme?
  • ClaimRole 是什么关系?
  • Policy 为什么还能写规则?
  • ClaimsPrincipalClaimsIdentity 又是什么?
  • 用户到底是怎么被塞进 HttpContext.User 里的?
  • 为什么有时候写 RequireRole("admin"),有时候写 RequireClaim("age", "18")

很多人学到这里,开始出现一个很大的问题:

代码记住了,但概念没建立起来。

一旦换个场景,比如:

  • Cookie 换 JWT
  • JWT 换 ApiKey
  • 要加管理员权限
  • 要按部门控制
  • 要做“必须是员工且年龄大于 18”

马上又乱了。

所以这篇文章不只是教你“写法”,而是帮你建立一套完整的认知模型。

你看完后,至少要真正明白:

  1. ASP.NET Core 认证授权到底分几层
  2. Scheme、Claim、Identity、Principal、Role、Policy 分别处在什么位置
  3. 它们之间是怎么串起来工作的
  4. 在 Minimal API 中分别应该怎么使用

二、先建立总图:ASP.NET Core 认证授权的全景图

如果你现在只想先有一个整体印象,可以先看这一句话:

认证是“你是谁”,授权是“你能干什么”。

ASP.NET Core 里,大致可以分成下面这几层:


2.1 第一层:认证方式(Scheme)

解决的是:

框架怎么从请求里识别用户?

比如:

  • 从 Cookie 里识别
  • 从 JWT 里识别
  • 从 Header 里识别

这就是 Scheme(认证方案)


2.2 第二层:用户身份(Identity / Principal)

认证成功以后,框架会构造一个“当前用户对象”,放到:

代码语言:javascript
复制
 HttpContext.User

这个对象就是:

  • ClaimsPrincipal
  • 它里面包含一个或多个 ClaimsIdentity

2.3 第三层:用户信息(Claim)

用户身份里包含很多“声明式信息”,比如:

  • 用户名
  • 用户 ID
  • 角色
  • 邮箱
  • 部门
  • 年龄

这些一条条的信息,就是:

Claim(声明)


2.4 第四层:权限判断(Authorization)

当接口要求:

  • 必须登录
  • 必须是 admin
  • 必须带某个 claim
  • 必须满足某组规则

这就是:

授权(Authorization)


2.5 第五层:授权规则(Policy)

当授权逻辑比较复杂时,你不想每个接口都重复写规则,于是可以定义:

Policy(策略)

例如:

  • 只有管理员可以访问
  • 必须是员工并且邮箱已验证
  • 必须有部门 claim 且值为 IT

三、先从最小单位开始:什么是 Claim?

这是整套体系里最基础的东西。


3.1 Claim 是什么?

Claim 可以翻译成:

  • 声明
  • 断言
  • 用户信息条目

你可以把它理解成:

关于当前用户的一条“已知事实”

例如:

  • 用户名是 alice
  • 用户 ID 是 1001
  • 用户角色是 admin
  • 部门是 IT
  • 邮箱是 alice@test.com

这些都可以表示成 Claim。


3.2 Claim 的代码长什么样?

代码语言:javascript
复制
 new Claim(ClaimTypes.Name, "alice")
 new Claim(ClaimTypes.NameIdentifier, "1001")
 new Claim(ClaimTypes.Role, "admin")
 new Claim("department", "IT")

每个 Claim 其实就是一个 键值对

  • Type:声明类型
  • Value:声明值

例如:

代码语言:javascript
复制
 new Claim("department", "IT")

表示:

  • 类型:department
  • 值:IT

3.3 你可以把 Claim 理解成“用户身上的标签”

例如某个用户有这些标签:

  • Name = alice
  • UserId = 1001
  • Role = admin
  • Department = IT

那么你后续做权限判断时,就可以说:

  • Role=admin 才能访问管理员接口
  • department=IT 才能访问 IT 系统
  • employee=true 才能访问员工中心

3.4 最小示例:构造 Claim

代码语言:javascript
复制
 using System.Security.Claims;
 ​
 var claims = new List<Claim>
 {
     new Claim(ClaimTypes.Name, "alice"),
     new Claim(ClaimTypes.NameIdentifier, "1001"),
     new Claim(ClaimTypes.Role, "admin"),
     new Claim("department", "IT")
 };

这就是在描述一个用户的基础资料。


四、第二步:什么是 ClaimsIdentity?

当你有了一堆 Claim 后,它们不能直接单独使用。 框架需要把这些 Claim 组合成一个“身份”。

这个身份对象就是:

ClaimsIdentity


4.1 Identity 是什么?

你可以把 ClaimsIdentity 理解成:

一组 Claim 组成的一份“身份信息”

例如:

代码语言:javascript
复制
 var identity = new ClaimsIdentity(claims, "Bearer");

它表示:

  • 这份身份里有若干 Claim
  • 这份身份是通过 "Bearer" 这个认证方案得到的

4.2 代码示例

代码语言:javascript
复制
 using System.Security.Claims;
 ​
 var claims = new List<Claim>
 {
     new Claim(ClaimTypes.Name, "alice"),
     new Claim(ClaimTypes.NameIdentifier, "1001"),
     new Claim(ClaimTypes.Role, "admin")
 };
 ​
 var identity = new ClaimsIdentity(claims, "CustomAuth");

此时 identity 里就有:

  • 用户名
  • 用户 ID
  • 角色
  • 认证类型 = CustomAuth

4.3 为什么要有 Identity 这层?

因为现实世界里,一个用户可能不止一种身份来源。

比如:

  • 一部分信息来自本地登录
  • 一部分信息来自外部身份提供者
  • 一部分信息来自附加授权系统

所以框架设计成:

  • Claim:单条信息
  • Identity:一组 Claim 组成的一份身份
  • Principal:一个用户,可包含多个 Identity

这比“只有一个简单用户对象”更灵活。


五、第三步:什么是 ClaimsPrincipal?

如果 ClaimsIdentity 是“一份身份”, 那 ClaimsPrincipal 就是:

当前用户整体

它是 ASP.NET Core 里最重要的用户对象。

框架最终会把它放到:

代码语言:javascript
复制
 HttpContext.User

5.1 Principal 和 Identity 的关系

可以简单理解为:

  • Claim:一条用户信息
  • ClaimsIdentity:一组用户信息,构成一个身份
  • ClaimsPrincipal:当前用户,里面可以包含一个或多个身份

5.2 代码示例

代码语言:javascript
复制
 using System.Security.Claims;
 ​
 var claims = new List<Claim>
 {
     new Claim(ClaimTypes.Name, "alice"),
     new Claim(ClaimTypes.Role, "admin")
 };
 ​
 var identity = new ClaimsIdentity(claims, "CustomAuth");
 var principal = new ClaimsPrincipal(identity);

此时:

  • principal.Identity.Namealice
  • principal.IsInRole("admin")true

5.3 Minimal API 中你最常接触的就是 ClaimsPrincipal

例如:

代码语言:javascript
复制
 app.MapGet("/me", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Name = user.Identity?.Name,
         IsAuthenticated = user.Identity?.IsAuthenticated
     });
 }).RequireAuthorization();

这里的 user,本质上就是 HttpContext.User


六、第四步:什么是 Role?它和 Claim 是什么关系?

这是很多人容易混淆的点。


6.1 Role 是什么?

Role 就是:

角色

比如:

  • admin
  • manager
  • user
  • superadmin

6.2 Role 本质上是不是特殊的 Claim?

是的。 在 ASP.NET Core 中,Role 本质上也是 Claim。

例如:

代码语言:javascript
复制
 new Claim(ClaimTypes.Role, "admin")

这就是一个角色 Claim。


6.3 为什么 Role 还单独拿出来讲?

因为“角色判断”太常见了,所以框架专门给它做了快捷支持。

例如:

代码语言:javascript
复制
 user.IsInRole("admin")

或者:

代码语言:javascript
复制
 policy.RequireRole("admin")

底层本质上,还是在看用户有没有:

代码语言:javascript
复制
 ClaimTypes.Role = "admin"

6.4 最小示例

代码语言:javascript
复制
 using System.Security.Claims;
 ​
 var claims = new List<Claim>
 {
     new Claim(ClaimTypes.Name, "alice"),
     new Claim(ClaimTypes.Role, "admin")
 };
 ​
 var identity = new ClaimsIdentity(claims, "Demo");
 var user = new ClaimsPrincipal(identity);
 ​
 Console.WriteLine(user.Identity?.Name);     // alice
 Console.WriteLine(user.IsInRole("admin"));  // true

七、第五步:什么是 Authentication Scheme?

现在回到你最开始困惑的 Scheme


7.1 Scheme 是什么?

Scheme 的中文一般翻译为:

  • 认证方案
  • 身份验证方案

它解决的问题是:

框架应该用哪种方式来识别当前用户?


7.2 常见 Scheme 例子

  • "Cookies":从 Cookie 中识别用户
  • "Bearer":从 JWT Bearer Token 中识别用户
  • "Simple":你自己写的自定义方案
  • "ApiKey":你自己写的 API Key 认证方案

7.3 为什么需要 Scheme?

因为一个系统可能有多种认证方式。

比如:

  • 浏览器用户用 Cookie
  • App 用户用 JWT
  • 内部服务用 ApiKey

如果没有 Scheme,框架就不知道:

当前该用哪一套逻辑去处理请求


7.4 代码示例

代码语言:javascript
复制
 builder.Services
     .AddAuthentication()
     .AddCookie("Cookies", options => { })
     .AddJwtBearer("Bearer", options => { });

这里你注册了两个方案:

  • Cookies
  • Bearer

之后授权时就可以指定某个接口该走哪个方案。


八、第六步:什么是认证(Authentication)?

现在把这些概念串起来。


8.1 认证在做什么?

认证做的事情是:

从请求里提取凭证,验证凭证有效性,构造当前用户对象

流程大致是:

  1. 请求来了
  2. 认证方案开始工作
  3. 从请求中读取 Cookie / Token / Header
  4. 如果凭证有效,就创建 ClaimsPrincipal
  5. 放入 HttpContext.User

8.2 认证成功后的结果是什么?

不是“返回 true/false”这么简单。 真正重要的结果是:

生成一个带 Claim 的用户对象

也就是:

代码语言:javascript
复制
 ClaimsPrincipal

8.3 认证失败呢?

如果凭证无效,或者没有凭证:

  • 可能返回 NoResult
  • 可能返回 Fail
  • 最终接口访问时可能变成 401

九、第七步:什么是授权(Authorization)?

如果说认证是在回答:

你是谁?

那么授权回答的是:

你能不能访问这个资源?


9.1 例子

比如一个接口:

代码语言:javascript
复制
 app.MapGet("/admin", () => "管理员接口")
    .RequireAuthorization();

这里至少要求:

  • 用户必须已认证

如果进一步写:

代码语言:javascript
复制
 .RequireAuthorization(policy => policy.RequireRole("admin"))

那要求就变成:

  • 用户不仅要已认证
  • 还必须有 admin 角色

9.2 认证和授权的区别

认证

识别身份

授权

根据身份和规则判断是否允许访问


9.3 一句话记忆

代码语言:javascript
复制
 Authentication:先认出你是谁
 Authorization:再判断你能做什么

十、第八步:什么是 Policy?

这一步就是把授权再往上提一层。


10.1 为什么要有 Policy?

如果你每个接口都手写规则:

代码语言:javascript
复制
 .RequireAuthorization(policy =>
 {
     policy.RequireAuthenticatedUser();
     policy.RequireRole("admin");
     policy.RequireClaim("department", "IT");
 });

写多了会很乱,也不好统一维护。

所以框架允许你把规则起个名字,集中定义:

这就是 Policy(策略)


10.2 Policy 的本质

Policy 本质上就是:

一组授权要求的组合

例如:

  • 必须登录
  • 必须是 admin
  • 必须有某个 claim
  • 必须满足自定义条件

10.3 最小 Policy 示例

代码语言:javascript
复制
 builder.Services.AddAuthorization(options =>
 {
     options.AddPolicy("AdminOnly", policy =>
     {
         policy.RequireAuthenticatedUser();
         policy.RequireRole("admin");
     });
 });

然后接口直接用:

代码语言:javascript
复制
 app.MapGet("/admin", () => "管理员接口")
    .RequireAuthorization("AdminOnly");

10.4 这样做的好处

  • 规则集中管理
  • 接口代码更简洁
  • 团队更容易看懂
  • 后续扩展更方便

十一、把整套关系串起来:一次请求到底经历了什么?

这部分非常关键。 你只有理解了请求执行链路,前面的概念才算真正串起来。


11.1 请求处理链路

假设你访问:

代码语言:javascript
复制
 GET /admin
 Authorization: Bearer xxx

系统里做了这些事:


第一步:认证中间件启动

代码语言:javascript
复制
 app.UseAuthentication();

开始工作。


第二步:按 Scheme 找认证处理器

例如 Bearer 方案:

  • 从请求头中读取 Token
  • 校验签名、过期时间等

第三步:认证成功后创建用户对象

认证通过后,框架会创建:

  • 一组 Claim
  • 一个 ClaimsIdentity
  • 一个 ClaimsPrincipal

然后放到:

代码语言:javascript
复制
 HttpContext.User

第四步:授权中间件启动

代码语言:javascript
复制
 app.UseAuthorization();

发现 /admin 这个接口要求授权。


第五步:执行 Policy 或角色规则

比如规则是:

  • 必须已登录
  • 必须有 admin 角色

框架就会检查:

  • user.Identity.IsAuthenticated
  • 是否存在 ClaimTypes.Role = admin

第六步:允许或拒绝访问

  • 满足条件:进入接口
  • 不满足:返回 401 或 403

十二、代码实战:用最小代码把 Claim、Identity、Principal、Role 串起来

这段代码不依赖 Web 项目,纯 C# 控制台也能看懂。

代码语言:javascript
复制
 using System.Security.Claims;
 ​
 // 1. 创建用户声明
 var claims = new List<Claim>
 {
     new Claim(ClaimTypes.Name, "alice"),
     new Claim(ClaimTypes.NameIdentifier, "1001"),
     new Claim(ClaimTypes.Role, "admin"),
     new Claim("department", "IT")
 };
 ​
 // 2. 创建身份
 var identity = new ClaimsIdentity(claims, "DemoAuth");
 ​
 // 3. 创建当前用户
 var principal = new ClaimsPrincipal(identity);
 ​
 // 4. 读取信息
 Console.WriteLine($"是否已认证: {principal.Identity?.IsAuthenticated}");
 Console.WriteLine($"用户名: {principal.Identity?.Name}");
 Console.WriteLine($"用户ID: {principal.FindFirst(ClaimTypes.NameIdentifier)?.Value}");
 Console.WriteLine($"部门: {principal.FindFirst("department")?.Value}");
 Console.WriteLine($"是否管理员: {principal.IsInRole("admin")}");

输出结果类似:

代码语言:javascript
复制
 是否已认证: True
 用户名: alice
 用户ID: 1001
 部门: IT
 是否管理员: True

十三、在 Minimal API 中最常见的几种写法分别是什么意思?


13.1 RequireAuthorization()

代码语言:javascript
复制
 app.MapGet("/me", () => "需要登录")
    .RequireAuthorization();

含义:

这个接口要求用户已认证


13.2 RequireRole("admin")

代码语言:javascript
复制
 .RequireAuthorization(policy => policy.RequireRole("admin"));

含义:

用户必须有 admin 角色


13.3 RequireClaim("department", "IT")

代码语言:javascript
复制
 .RequireAuthorization(policy => policy.RequireClaim("department", "IT"));

含义:

用户必须带 department=IT 这个 Claim


13.4 ClaimsPrincipal user

代码语言:javascript
复制
 app.MapGet("/me", (ClaimsPrincipal user) => ...)

含义:

直接注入当前登录用户对象


十四、避坑要点:初学者最容易混淆的地方


14.1 Claim 和 Role 不是完全平级概念

Role 本质上是 Claim 的一种特殊约定。 所以:

  • 所有 Role 都可以看成 Claim
  • 但不是所有 Claim 都是 Role

14.2 ClaimsIdentity 不是“整个用户”

整个用户通常是:

代码语言:javascript
复制
 ClaimsPrincipal

ClaimsIdentity 只是其中一个身份来源。


14.3 Scheme 不是权限规则

Scheme 解决的是:

怎么识别用户

Policy / Role / Claim 规则解决的是:

用户识别出来以后,能不能访问


14.4 认证成功 ≠ 一定有权限

一个用户就算通过认证了,也可能没有访问某接口的权限。 这就是为什么会有:

  • 401:没认证
  • 403:认证了,但没权限

十五、性能 / 架构建议:这套体系在项目里怎么用最稳?


15.1 小项目先记住最核心四件事

如果你是初学者,先把这四个吃透:

  1. Scheme:认证方式
  2. Claim:用户信息
  3. Principal:当前用户
  4. Policy:授权规则组合

这四个掌握后,其他概念就容易了。


15.2 不要一开始就定义一堆复杂 Policy

刚开始只需要:

  • 是否登录
  • 是否管理员
  • 是否有某个 claim

先把最常见场景跑通,再慢慢扩展。


15.3 业务规则复杂时,优先抽 Policy

例如:

  • 财务角色且部门为 Finance
  • 员工状态有效且邮箱已验证

不要把这些逻辑散落在所有接口上,应该统一定义成 Policy。


15.4 角色适合粗粒度,Claim 适合细粒度

Role

适合:

  • admin
  • manager
  • user

Claim

适合:

  • department=IT
  • level=3
  • employeeId=1001
  • region=CN

换句话说:

  • Role 用来区分大类身份
  • Claim 用来表达细节属性

十六、总结:你应该怎么记这整套体系?

最后我帮你用一套最简单的方式收束一下。


16.1 先记对象关系

代码语言:javascript
复制
 Claim -> ClaimsIdentity -> ClaimsPrincipal

含义是:

  • Claim:一条用户信息
  • ClaimsIdentity:一组信息形成一个身份
  • ClaimsPrincipal:当前用户整体

16.2 再记认证和授权

代码语言:javascript
复制
 Scheme 负责认证
 Policy / Role / Claim 负责授权

16.3 再记请求过程

代码语言:javascript
复制
 请求进来
 -> Scheme 识别用户
 -> 构造 ClaimsPrincipal
 -> 放入 HttpContext.User
 -> Authorization 按 Policy / Role / Claim 检查
 -> 允许或拒绝访问

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、痛点引入:为什么明明代码能跑,却还是看不懂?
  • 二、先建立总图:ASP.NET Core 认证授权的全景图
    • 2.1 第一层:认证方式(Scheme)
    • 2.2 第二层:用户身份(Identity / Principal)
    • 2.3 第三层:用户信息(Claim)
    • 2.4 第四层:权限判断(Authorization)
    • 2.5 第五层:授权规则(Policy)
  • 三、先从最小单位开始:什么是 Claim?
    • 3.1 Claim 是什么?
    • 3.2 Claim 的代码长什么样?
    • 3.3 你可以把 Claim 理解成“用户身上的标签”
    • 3.4 最小示例:构造 Claim
  • 四、第二步:什么是 ClaimsIdentity?
    • 4.1 Identity 是什么?
    • 4.2 代码示例
    • 4.3 为什么要有 Identity 这层?
  • 五、第三步:什么是 ClaimsPrincipal?
    • 5.1 Principal 和 Identity 的关系
    • 5.2 代码示例
    • 5.3 Minimal API 中你最常接触的就是 ClaimsPrincipal
  • 六、第四步:什么是 Role?它和 Claim 是什么关系?
    • 6.1 Role 是什么?
    • 6.2 Role 本质上是不是特殊的 Claim?
    • 6.3 为什么 Role 还单独拿出来讲?
    • 6.4 最小示例
  • 七、第五步:什么是 Authentication Scheme?
    • 7.1 Scheme 是什么?
    • 7.2 常见 Scheme 例子
    • 7.3 为什么需要 Scheme?
    • 7.4 代码示例
  • 八、第六步:什么是认证(Authentication)?
    • 8.1 认证在做什么?
    • 8.2 认证成功后的结果是什么?
    • 8.3 认证失败呢?
  • 九、第七步:什么是授权(Authorization)?
    • 9.1 例子
    • 9.2 认证和授权的区别
      • 认证
      • 授权
    • 9.3 一句话记忆
  • 十、第八步:什么是 Policy?
    • 10.1 为什么要有 Policy?
    • 10.2 Policy 的本质
    • 10.3 最小 Policy 示例
    • 10.4 这样做的好处
  • 十一、把整套关系串起来:一次请求到底经历了什么?
    • 11.1 请求处理链路
      • 第一步:认证中间件启动
      • 第二步:按 Scheme 找认证处理器
      • 第三步:认证成功后创建用户对象
      • 第四步:授权中间件启动
      • 第五步:执行 Policy 或角色规则
      • 第六步:允许或拒绝访问
  • 十二、代码实战:用最小代码把 Claim、Identity、Principal、Role 串起来
  • 十三、在 Minimal API 中最常见的几种写法分别是什么意思?
    • 13.1 RequireAuthorization()
    • 13.2 RequireRole("admin")
    • 13.3 RequireClaim("department", "IT")
    • 13.4 ClaimsPrincipal user
  • 十四、避坑要点:初学者最容易混淆的地方
    • 14.1 Claim 和 Role 不是完全平级概念
    • 14.2 ClaimsIdentity 不是“整个用户”
    • 14.3 Scheme 不是权限规则
    • 14.4 认证成功 ≠ 一定有权限
  • 十五、性能 / 架构建议:这套体系在项目里怎么用最稳?
    • 15.1 小项目先记住最核心四件事
    • 15.2 不要一开始就定义一堆复杂 Policy
    • 15.3 业务规则复杂时,优先抽 Policy
    • 15.4 角色适合粗粒度,Claim 适合细粒度
      • Role
      • Claim
  • 十六、总结:你应该怎么记这整套体系?
    • 16.1 先记对象关系
    • 16.2 再记认证和授权
    • 16.3 再记请求过程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档