如何从ASP.NET Core多认证方案中获取所有Claim?
多阶段身份验证问题:无法合并多个认证Scheme的Claim
我们需要实现多阶段身份验证流程:先以普通用户身份通过scheme1(Cookie有效期较长)完成登录,再额外以超级用户身份通过scheme2(Cookie有效期较短)登录。但目前遇到问题:调用/info接口时,仅能获取到scheme1中的Claim,无法同时获取两个认证方案的Claim。
问题复现代码
using System.Security.Claims; using Microsoft.AspNetCore.Authentication; const string scheme1 = "cookie1"; const string scheme2 = "cookie2"; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(scheme1) .AddCookie(scheme1) .AddCookie(scheme2); var app = builder.Build(); app.UseAuthentication(); app.MapGet("/info", (HttpContext ctx) => string.Join(", ", ctx.User.Claims.Select(c => $"[{c}]"))); app.MapGet("/login", async (HttpContext ctx) => { await ctx.SignInAsync(scheme1, new(new ClaimsIdentity([new("key1", "val1")], "id1"))); await ctx.SignInAsync(scheme2, new(new ClaimsIdentity([new("key2", "val2")], "id2"))); return "multistage login simulation"; }); app.Run();
问题原因
ASP.NET Core默认只会自动验证配置的默认认证Scheme(此处为scheme1),不会主动触发其他Scheme的身份验证流程,因此ctx.User仅包含默认Scheme解析出的Claim。
解决方案
方案1:接口内手动验证并合并Claim
修改/info接口逻辑,手动验证scheme2并合并两个Scheme的Claim:
app.MapGet("/info", async (HttpContext ctx) => { // 获取默认scheme1的身份信息 var identity1 = ctx.User.Identities.FirstOrDefault(); // 手动验证scheme2 var result2 = await ctx.AuthenticateAsync(scheme2); var identity2 = result2.Principal?.Identities.FirstOrDefault(); // 合并两个身份的Claim var allClaims = new List<Claim>(); if (identity1 != null) allClaims.AddRange(identity1.Claims); if (identity2 != null) allClaims.AddRange(identity2.Claims); return string.Join(", ", allClaims.Select(c => $"[{c}]")); });
方案2:全局中间件处理多Scheme验证
如果多个接口都需要合并多Scheme的Claim,可添加自定义中间件统一处理:
var app = builder.Build(); // 自定义中间件:验证多个Scheme并合并身份 app.Use(async (context, next) => { // 执行默认scheme1的认证 await context.AuthenticateAsync(scheme1); // 手动验证scheme2 var scheme2Result = await context.AuthenticateAsync(scheme2); if (scheme2Result.Succeeded && scheme2Result.Principal != null) { // 将scheme2的身份合并到当前User中 context.User.AddIdentity(scheme2Result.Principal.Identities.First()); } await next(); }); app.MapGet("/info", (HttpContext ctx) => string.Join(", ", ctx.User.Claims.Select(c => $"[{c}]"))); // 登录接口保持不变 app.MapGet("/login", async (HttpContext ctx) => { await ctx.SignInAsync(scheme1, new(new ClaimsIdentity([new("key1", "val1")], "id1"))); await ctx.SignInAsync(scheme2, new(new ClaimsIdentity([new("key2", "val2")], "id2"))); return "multistage login simulation"; }); app.Run();
内容的提问来源于stack exchange,提问作者user23207089
相关产品推荐
相关产品推荐

