首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Minimal API 多认证方案详解:什么时候需要多个认证方案?怎么配置?怎么选择?

Minimal API 多认证方案详解:什么时候需要多个认证方案?怎么配置?怎么选择?

原创
作者头像
步步为营DotNet
发布2026-07-09 17:41:31
发布2026-07-09 17:41:31
80
举报

这篇文章专门回答一个在 Minimal API 中非常关键、但很多人一开始容易糊涂的问题: 一个项目里如果有多个认证方案,该怎么办?如何选择?什么情况下采用哪种方式?

这不是一个“会不会写代码”的问题,而是一个 框架理解 + 架构设计 + 落地写法 的问题。

很多人刚学 Minimal API 时,先接触的是单一认证方案:

  • 要么全站都是 Cookie
  • 要么全站都是 JWT

这当然没问题。 但一旦项目稍微真实一点,就会出现这些需求:

  • 管理后台给浏览器用,适合 Cookie
  • 移动端 / 前端 SPA 调 API,适合 JWT
  • 某些内部接口要走自定义 Header 认证
  • 第三方回调接口可能用专门的签名认证
  • 某些接口同时允许 Cookie 和 Bearer 任意一种通过

这时候你就不能只会:

代码语言:javascript
复制
 builder.Services.AddAuthentication("Bearer").AddJwtBearer(...);

你必须真正理解:

  1. 什么是多个认证方案
  2. 多个认证方案在 Minimal API 中如何注册
  3. 框架默认会用哪一个
  4. 某个接口如何指定用哪个方案
  5. 什么时候应该使用多个认证方案
  6. 什么时候反而不要乱上多个认证方案

这篇文章我会按 从简单到难 的顺序来讲。


一、痛点引入:为什么会需要多个认证方案?

先别急着写代码,我们先看现实项目。


1.1 场景一:后台管理系统 + 前后端 API 共存

你有一个系统:

  • 管理员在浏览器中访问后台管理页面
  • 浏览器天然适合 Cookie 登录
  • 同时你的系统还给小程序 / App / Vue 前端提供 API
  • 这些客户端更适合用 JWT

这时一个项目里就可能同时需要:

  • Cookie 认证
  • JWT Bearer 认证

1.2 场景二:内部系统调用接口

比如某个内部系统访问你的接口,不走用户登录,而是带一个固定 Header:

代码语言:javascript
复制
 X-Internal-Key: abc123

这就可能需要第三种方案:

  • 自定义 Header 认证

1.3 场景三:同一个接口允许不同客户端访问

比如 /api/profile

  • 浏览器用户已经登录了 Cookie,可以访问
  • App 用户带 JWT,也可以访问

那么同一个接口可能要支持:

  • Cookie
  • JWT

任意一种都算通过。


1.4 所以“多个认证方案”本质上是在解决什么问题?

本质上是:

不同来源的请求,携带身份凭证的方式不一样。

比如:

  • 浏览器靠 Cookie
  • App 靠 Bearer Token
  • 内部系统靠自定义 Header
  • 网关可能透传某种身份头

ASP.NET Core 认证框架就是用“认证方案(Scheme)”来区分这些不同的识别方式。


二、先把最核心概念彻底讲明白:什么叫“多个认证方案”?

很多初学者会误以为:

多个认证方案 = 有多个用户系统

其实不是。

多个认证方案真正的含义是:

同一个 ASP.NET Core / Minimal API 应用中,存在多种“识别用户身份”的方式。

例如:

  • 方案 A:从 Cookie 中识别用户
  • 方案 B:从 Authorization: Bearer 中识别用户
  • 方案 C:从 X-Api-Key 中识别调用方

它们并不一定代表三套用户体系,而是三套 身份提取规则


2.1 你可以把认证方案理解成“识别身份证明的方法”

比如:

Cookie 方案

看请求里有没有认证 Cookie

JWT Bearer 方案

看请求头里有没有 Bearer Token

自定义方案

看请求头里有没有某个内部密钥

所以“多个认证方案”的意思是:

项目里注册了多种识别方法,框架可以按规则选择其中一种或多种来执行。


三、在 Minimal API 中,多个认证方案是怎么注册的?

这部分先看最基础的代码。


3.1 同时注册 Cookie 和 JWT

代码语言:javascript
复制
 using Microsoft.AspNetCore.Authentication.Cookies;
 using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.IdentityModel.Tokens;
 using System.Text;
 ​
 var builder = WebApplication.CreateBuilder(args);
 ​
 var secretKey = "this_is_a_demo_secret_key_1234567890";
 ​
 builder.Services
     .AddAuthentication()
     .AddCookie("Cookies", options =>
     {
         options.Cookie.Name = "demo.cookie";
 ​
         options.Events.OnRedirectToLogin = context =>
         {
             context.Response.StatusCode = 401;
             return Task.CompletedTask;
         };
     })
     .AddJwtBearer("Bearer", options =>
     {
         options.TokenValidationParameters = new TokenValidationParameters
         {
             ValidateIssuer = false,
             ValidateAudience = false,
             ValidateIssuerSigningKey = true,
             IssuerSigningKey = new SymmetricSecurityKey(
                 Encoding.UTF8.GetBytes(secretKey)),
             ValidateLifetime = true
         };
     });
 ​
 builder.Services.AddAuthorization();
 ​
 var app = builder.Build();
 ​
 app.UseAuthentication();
 app.UseAuthorization();
 ​
 app.MapGet("/", () => "公开接口");
 ​
 app.Run();

3.2 这段代码和单一方案最大的差别是什么?

你会发现,这次不是:

代码语言:javascript
复制
 builder.Services.AddAuthentication("Bearer")

而是:

代码语言:javascript
复制
 builder.Services.AddAuthentication()

也就是:

先不指定默认方案,只把多个方案都注册进去。

然后分别注册:

  • "Cookies"
  • "Bearer"

四、为什么有多个认证方案时,往往不急着设置默认方案?

因为一旦你设置了默认方案,框架在很多场景下会优先走它。

而多个方案并存时,常常需要:

  • 某些接口用 Cookie
  • 某些接口用 Bearer
  • 某些接口允许两者都行

这时如果你一开始就设一个全局默认方案,很容易让行为变得不直观。

所以对于多方案项目,一个很常见的做法是:

代码语言:javascript
复制
 builder.Services.AddAuthentication();

然后在具体接口上明确指定方案。


五、在 Minimal API 中,某个接口怎么指定使用哪种认证方案?

这是多方案最关键的地方。


5.1 让某个接口只接受 Cookie

代码语言:javascript
复制
 app.MapGet("/cookie-only", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Message = "你通过 Cookie 登录了",
         Name = user.Identity?.Name
     });
 })
 .RequireAuthorization(policy =>
 {
     policy.AuthenticationSchemes.Add("Cookies");
     policy.RequireAuthenticatedUser();
 });

这里的意思是:

  • 这个接口只尝试用 "Cookies" 方案认证
  • 并要求用户必须已认证

5.2 让某个接口只接受 JWT

代码语言:javascript
复制
 app.MapGet("/jwt-only", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Message = "你通过 JWT 登录了",
         Name = user.Identity?.Name
     });
 })
 .RequireAuthorization(policy =>
 {
     policy.AuthenticationSchemes.Add("Bearer");
     policy.RequireAuthenticatedUser();
 });

5.3 让某个接口同时接受 Cookie 或 JWT

代码语言:javascript
复制
 app.MapGet("/hybrid", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Message = "Cookie 或 JWT 任一通过即可",
         Name = user.Identity?.Name
     });
 })
 .RequireAuthorization(policy =>
 {
     policy.AuthenticationSchemes.Add("Cookies");
     policy.AuthenticationSchemes.Add("Bearer");
     policy.RequireAuthenticatedUser();
 });

这表示:

这个接口允许用 "Cookies""Bearer" 任意一种方案完成认证。


六、一个完整可运行示例:Minimal API 同时支持 Cookie 和 JWT

下面我给你一个更完整但仍然适合初学者理解的版本。

这个例子包含:

  • Cookie 登录接口
  • JWT 登录接口
  • 只允许 Cookie 的接口
  • 只允许 JWT 的接口
  • 两者都允许的接口

6.1 完整代码

代码语言:javascript
复制
 using System.IdentityModel.Tokens.Jwt;
 using System.Security.Claims;
 using System.Text;
 using Microsoft.AspNetCore.Authentication.Cookies;
 using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.IdentityModel.Tokens;
 ​
 var builder = WebApplication.CreateBuilder(args);
 ​
 var secretKey = "this_is_a_demo_secret_key_1234567890";
 ​
 // 注册多个认证方案
 builder.Services
     .AddAuthentication()
     .AddCookie("Cookies", options =>
     {
         options.Cookie.Name = "demo.cookie";
 ​
         options.Events.OnRedirectToLogin = context =>
         {
             context.Response.StatusCode = StatusCodes.Status401Unauthorized;
             return Task.CompletedTask;
         };
 ​
         options.Events.OnRedirectToAccessDenied = context =>
         {
             context.Response.StatusCode = StatusCodes.Status403Forbidden;
             return Task.CompletedTask;
         };
     })
     .AddJwtBearer("Bearer", options =>
     {
         options.TokenValidationParameters = new TokenValidationParameters
         {
             ValidateIssuer = false,
             ValidateAudience = false,
             ValidateIssuerSigningKey = true,
             IssuerSigningKey = new SymmetricSecurityKey(
                 Encoding.UTF8.GetBytes(secretKey)),
             ValidateLifetime = true,
             ClockSkew = TimeSpan.Zero
         };
     });
 ​
 builder.Services.AddAuthorization();
 ​
 var app = builder.Build();
 ​
 app.UseAuthentication();
 app.UseAuthorization();
 ​
 app.MapGet("/", () => "公开接口");
 ​
 // Cookie 登录
 app.MapPost("/login-cookie", async (HttpContext context) =>
 {
     var claims = new List<Claim>
     {
         new Claim(ClaimTypes.Name, "cookie-user"),
         new Claim(ClaimTypes.NameIdentifier, "1001")
     };
 ​
     var identity = new ClaimsIdentity(claims, "Cookies");
     var principal = new ClaimsPrincipal(identity);
 ​
     await context.SignInAsync("Cookies", principal);
 ​
     return Results.Ok(new { message = "Cookie 登录成功" });
 });
 ​
 // JWT 登录
 app.MapPost("/login-jwt", () =>
 {
     var claims = new List<Claim>
     {
         new Claim(ClaimTypes.Name, "jwt-user"),
         new Claim(ClaimTypes.NameIdentifier, "2001")
     };
 ​
     var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
     var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
 ​
     var token = new JwtSecurityToken(
         claims: claims,
         expires: DateTime.UtcNow.AddMinutes(30),
         signingCredentials: credentials);
 ​
     var jwt = new JwtSecurityTokenHandler().WriteToken(token);
 ​
     return Results.Ok(new
     {
         access_token = jwt
     });
 });
 ​
 // 只允许 Cookie
 app.MapGet("/cookie-only", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Message = "这是 Cookie 专属接口",
         Name = user.Identity?.Name
     });
 })
 .RequireAuthorization(policy =>
 {
     policy.AuthenticationSchemes.Add("Cookies");
     policy.RequireAuthenticatedUser();
 });
 ​
 // 只允许 JWT
 app.MapGet("/jwt-only", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Message = "这是 JWT 专属接口",
         Name = user.Identity?.Name
     });
 })
 .RequireAuthorization(policy =>
 {
     policy.AuthenticationSchemes.Add("Bearer");
     policy.RequireAuthenticatedUser();
 });
 ​
 // Cookie 或 JWT 都可以
 app.MapGet("/both", (ClaimsPrincipal user) =>
 {
     return Results.Ok(new
     {
         Message = "Cookie 或 JWT 都可以访问",
         Name = user.Identity?.Name
     });
 })
 .RequireAuthorization(policy =>
 {
     policy.AuthenticationSchemes.Add("Cookies");
     policy.AuthenticationSchemes.Add("Bearer");
     policy.RequireAuthenticatedUser();
 });
 ​
 app.Run();

七、怎么选择:什么情况下用单一方案,什么情况下用多个认证方案?

这一节最重要。 你不是只要会写代码,更要知道 何时应该这样设计


7.1 情况一:只有浏览器后台管理系统

比如:

  • 一个管理后台
  • 用户只在浏览器登录
  • 所有请求都来自同一个站点页面

推荐方案

只用 Cookie

为什么?

因为:

  • 浏览器天然支持 Cookie
  • 登录、退出、续期都很自然
  • 不需要前端处理 Token
  • 安全模型和浏览器场景更契合

这时候要不要多方案?

一般 不要

因为没有必要引入复杂度。


7.2 情况二:标准前后端分离 API

比如:

  • Vue / React 前端
  • App / 小程序
  • 前端自己调用 API
  • 不依赖服务端页面

推荐方案

只用 JWT Bearer

为什么?

因为:

  • 客户端容易管理 Token
  • 更适合跨端调用
  • 不依赖浏览器 Cookie 行为
  • 更适合开放式 API

这时候要不要多方案?

一般 不要

因为单一 Bearer 已经足够清晰。


7.3 情况三:一个项目同时服务浏览器后台和开放 API

比如:

  • /admin/* 给后台管理页面
  • /api/* 给前端 / App 调用

推荐方案

Cookie + JWT 多方案

为什么?

因为两个调用场景天然不同:

  • 浏览器后台:更适合 Cookie
  • 前后端 API:更适合 JWT

这是最常见、也最合理的多方案场景

你可以这么分:

  • /admin/* 走 Cookie
  • /api/* 走 Bearer

7.4 情况四:内部系统调用 + 用户系统共存

比如:

  • 用户访问接口靠 JWT
  • 内部 Job、网关、系统间调用靠 API Key / 自定义 Header

推荐方案

JWT + 自定义方案

为什么?

因为调用方不是“人”,而是“系统”。 系统调用未必需要用户登录流程,更适合走单独的机器认证方式。


7.5 情况五:同一个接口既给浏览器,也给 App 用

比如:

  • /profile
  • 浏览器用户已经 Cookie 登录
  • App 用户带 Bearer Token
  • 两种方式都应允许访问

推荐方案

混合支持

代码语言:javascript
复制
 policy.AuthenticationSchemes.Add("Cookies");
 policy.AuthenticationSchemes.Add("Bearer");

但要注意

这类设计虽然可行,但会增加接口行为复杂度。 如果能从路由层面分开,通常更清晰。


八、多个认证方案时,应该怎么做选择?我给你一个简单决策表


8.1 选型表

场景

推荐方案

纯浏览器后台

Cookie

纯前后端分离 API

JWT Bearer

后台管理 + API 共存

Cookie + JWT

用户认证 + 系统间调用

JWT + 自定义 Header / ApiKey

同一个接口服务多个客户端

可考虑多方案并行


8.2 一个更实用的判断标准

问自己三个问题:

问题 1:请求主要来自哪里?

  • 浏览器页面:偏 Cookie
  • App / SPA / 第三方:偏 JWT

问题 2:凭证由谁保存和携带?

  • 浏览器自动带:Cookie
  • 客户端自己存储并放 Header:JWT

问题 3:是否存在明显不同的调用方?

  • 用户
  • 管理员
  • 内部系统
  • 第三方平台

如果有不同调用方,往往就可能要多个方案。


九、Minimal API 中多个认证方案的常见组织方式

多方案一多,最怕的是 Program.cs 里全乱掉。

建议这样组织。


9.1 按路由前缀分组

代码语言:javascript
复制
 var adminGroup = app.MapGroup("/admin")
     .RequireAuthorization(policy =>
     {
         policy.AuthenticationSchemes.Add("Cookies");
         policy.RequireAuthenticatedUser();
     });
 ​
 var apiGroup = app.MapGroup("/api")
     .RequireAuthorization(policy =>
     {
         policy.AuthenticationSchemes.Add("Bearer");
         policy.RequireAuthenticatedUser();
     });

这样你的接口天然分区:

  • /admin/* 全走 Cookie
  • /api/* 全走 JWT

这在 Minimal API 中非常推荐。


9.2 把授权规则抽成 Policy

例如:

代码语言:javascript
复制
 builder.Services.AddAuthorization(options =>
 {
     options.AddPolicy("CookieOnly", policy =>
     {
         policy.AuthenticationSchemes.Add("Cookies");
         policy.RequireAuthenticatedUser();
     });
 ​
     options.AddPolicy("BearerOnly", policy =>
     {
         policy.AuthenticationSchemes.Add("Bearer");
         policy.RequireAuthenticatedUser();
     });
 ​
     options.AddPolicy("CookieOrBearer", policy =>
     {
         policy.AuthenticationSchemes.Add("Cookies");
         policy.AuthenticationSchemes.Add("Bearer");
         policy.RequireAuthenticatedUser();
     });
 });

然后接口上这么写:

代码语言:javascript
复制
 app.MapGet("/cookie-data", () => "cookie data")
    .RequireAuthorization("CookieOnly");
 ​
 app.MapGet("/jwt-data", () => "jwt data")
    .RequireAuthorization("BearerOnly");
 ​
 app.MapGet("/hybrid-data", () => "hybrid data")
    .RequireAuthorization("CookieOrBearer");

这比把认证方案写死在每个接口里更清晰。


十、多个认证方案时最常见的坑


10.1 以为注册多个方案后,接口会自动识别所有方案

不是的。

如果你没有明确指定,框架会按默认方案或默认行为走。 这可能不是你想要的。

所以多方案项目里,最好明确指定每个接口或每组接口使用哪个方案


10.2 不设置默认方案,又希望 [全局] 自动认证

如果你完全不指定默认方案,也不在接口上声明具体方案,某些行为会变得不明确。 所以多方案项目要么:

  • 明确每个接口使用哪个方案
  • 要么设置一个你最常用的默认方案

10.3 一个接口允许多个方案后,调试时不知道到底走了哪个

这是实战中经常让人困惑的点。 比如一个接口允许:

  • Cookies
  • Bearer

如果两个凭证都带了,最后到底是谁生效?

这取决于认证处理和授权组合方式。 所以对于业务系统,我一般建议:

能按路由分开就分开,不要轻易让同一接口承载太多认证入口。

否则排查问题会更复杂。


10.4 Cookie 和 JWT 的未授权行为不一样

Cookie 默认喜欢跳转页面;JWT 默认返回 401。 多方案项目里如果不统一处理,接口返回行为会不一致。

所以通常要显式处理 Cookie 的跳转事件。


十一、性能 / 架构建议:什么时候该上多方案,什么时候别乱上?


11.1 没有明确业务需求,不要为了“高级”而上多方案

这是最重要的一条。

很多初学者觉得:

一个项目支持 Cookie + JWT + ApiKey,好像很厉害

但实际上:

  • 配置复杂度上升
  • 调试成本上升
  • 接口行为更难预测
  • 团队理解成本更高

如果你只是普通后台项目,单 Cookie 就很好。 如果你只是标准 API,单 JWT 就很好。


11.2 多方案最合理的场景:确实存在不同调用类型

比如:

  • 浏览器用户
  • App 用户
  • 内部服务调用

如果调用模式天然不同,那么多方案是合理的。


11.3 最推荐的多方案架构:按边界隔离

比如:

  • /admin/*:Cookie
  • /api/*:JWT
  • /internal/*:ApiKey / 自定义 Header

这种按路由边界分隔的做法,远比所有接口都混合支持更可维护。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、痛点引入:为什么会需要多个认证方案?
    • 1.1 场景一:后台管理系统 + 前后端 API 共存
    • 1.2 场景二:内部系统调用接口
    • 1.3 场景三:同一个接口允许不同客户端访问
    • 1.4 所以“多个认证方案”本质上是在解决什么问题?
  • 二、先把最核心概念彻底讲明白:什么叫“多个认证方案”?
    • 2.1 你可以把认证方案理解成“识别身份证明的方法”
      • Cookie 方案
      • JWT Bearer 方案
      • 自定义方案
  • 三、在 Minimal API 中,多个认证方案是怎么注册的?
    • 3.1 同时注册 Cookie 和 JWT
    • 3.2 这段代码和单一方案最大的差别是什么?
  • 四、为什么有多个认证方案时,往往不急着设置默认方案?
  • 五、在 Minimal API 中,某个接口怎么指定使用哪种认证方案?
    • 5.1 让某个接口只接受 Cookie
    • 5.2 让某个接口只接受 JWT
    • 5.3 让某个接口同时接受 Cookie 或 JWT
  • 六、一个完整可运行示例:Minimal API 同时支持 Cookie 和 JWT
    • 6.1 完整代码
  • 七、怎么选择:什么情况下用单一方案,什么情况下用多个认证方案?
    • 7.1 情况一:只有浏览器后台管理系统
      • 推荐方案
      • 为什么?
      • 这时候要不要多方案?
    • 7.2 情况二:标准前后端分离 API
      • 推荐方案
      • 为什么?
      • 这时候要不要多方案?
    • 7.3 情况三:一个项目同时服务浏览器后台和开放 API
      • 推荐方案
      • 为什么?
      • 这是最常见、也最合理的多方案场景
    • 7.4 情况四:内部系统调用 + 用户系统共存
      • 推荐方案
      • 为什么?
    • 7.5 情况五:同一个接口既给浏览器,也给 App 用
      • 推荐方案
      • 但要注意
  • 八、多个认证方案时,应该怎么做选择?我给你一个简单决策表
    • 8.1 选型表
    • 8.2 一个更实用的判断标准
      • 问题 1:请求主要来自哪里?
      • 问题 2:凭证由谁保存和携带?
      • 问题 3:是否存在明显不同的调用方?
  • 九、Minimal API 中多个认证方案的常见组织方式
    • 9.1 按路由前缀分组
    • 9.2 把授权规则抽成 Policy
  • 十、多个认证方案时最常见的坑
    • 10.1 以为注册多个方案后,接口会自动识别所有方案
    • 10.2 不设置默认方案,又希望 [全局] 自动认证
    • 10.3 一个接口允许多个方案后,调试时不知道到底走了哪个
    • 10.4 Cookie 和 JWT 的未授权行为不一样
  • 十一、性能 / 架构建议:什么时候该上多方案,什么时候别乱上?
    • 11.1 没有明确业务需求,不要为了“高级”而上多方案
    • 11.2 多方案最合理的场景:确实存在不同调用类型
    • 11.3 最推荐的多方案架构:按边界隔离
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档