如何在 ASP.NET Core 2.0 中设置多个身份验证方案?

Posted

技术标签:

【中文标题】如何在 ASP.NET Core 2.0 中设置多个身份验证方案?【英文标题】:How do I setup multiple auth schemes in ASP.NET Core 2.0? 【发布时间】:2018-01-23 12:34:16 【问题描述】:

我正在尝试将我的身份验证内容迁移到 Core 2.0,但在使用我自己的身份验证方案时遇到了问题。我在启动时的服务设置如下所示:

var authenticationBuilder = services.AddAuthentication(options =>

    options.AddScheme("myauth", builder =>
    
        builder.HandlerType = typeof(CookieAuthenticationHandler);
    );
)
    .AddCookie();

我在控制器中的登录代码如下所示:

var claims = new List<Claim>

    new Claim(ClaimTypes.Name, user.Name)
;

var props = new AuthenticationProperties

    IsPersistent = persistCookie,
    ExpiresUtc = DateTime.UtcNow.AddYears(1)
;

var id = new ClaimsIdentity(claims);
await HttpContext.SignInAsync("myauth", new ClaimsPrincipal(id), props);

但是当我在控制器或动作过滤器中时,我只有一个身份,而且不是经过身份验证的身份:

var identity = context.HttpContext.User.Identities.SingleOrDefault(x => x.AuthenticationType == "myauth");

浏览这些更改很困难,但我猜我做错了 .AddScheme。有什么建议吗?

编辑:这是(本质上)一个干净的应用程序,它不会在 User.Identies 上产生两组身份:

namespace WebApplication1.Controllers

    public class Testy : Controller
    
        public IActionResult Index()
        
            var i = HttpContext.User.Identities;
            return Content("index");
        

        public async Task<IActionResult> In1()
        
            var claims = new List<Claim>  new Claim(ClaimTypes.Name, "In1 name") ;
            var props = new AuthenticationProperties   IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddYears(1) ;
            var id = new ClaimsIdentity(claims);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id), props);
            return Content("In1");
        

        public async Task<IActionResult> In2()
        
            var claims = new List<Claim>  new Claim(ClaimTypes.Name, "a2 name") ;
            var props = new AuthenticationProperties  IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddYears(1) ;
            var id = new ClaimsIdentity(claims);
            await HttpContext.SignInAsync("a2", new ClaimsPrincipal(id), props);
            return Content("In2");
        

        public async Task<IActionResult> Out1()
        
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return Content("Out1");
        

        public async Task<IActionResult> Out2()
        
            await HttpContext.SignOutAsync("a2");
            return Content("Out2");
        
    

和启动:

namespace WebApplication1

    public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        public void ConfigureServices(IServiceCollection services)
        
            services.AddAuthentication(options =>
            
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                )
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie("a2");

            services.AddMvc();
        

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        
            app.UseAuthentication();

            app.UseMvc(routes =>
            
                routes.MapRoute(name: "default", template: "controller=Home/action=Index/id?");
            );
        
    

【问题讨论】:

我们按照 IdentityServer4 快速入门中的这个示例注册了我们的方案:github.com/IdentityServer/IdentityServer4.Samples/blob/release/… 这与 Core 2.0 无关。 【参考方案1】:

2019 年 12 月的编辑:请先考虑这个答案: Use multiple JWT Bearer Authentication

我的旧答案(不适合使用多个 JWT,而只适合使用 JWT + API 密钥,正如用户评论的那样):

另一种可能性是在运行时确定要选择哪种身份验证策略方案,我曾遇到过可以拥有 http 身份验证承载令牌标头或 cookie 的情况。

所以,感谢https://github.com/aspnet/Security/issues/1469

请求标头中有 JWT 令牌,然后是 OpenIdConnect (Azure AD) 或其他任何内容。

public void ConfigureServices(IServiceCollection services)
    
        // Add CORS
        services.AddCors();

        // Add authentication before adding MVC
        // Add JWT and Azure AD (that uses OpenIdConnect) and cookies.
        // Use a smart policy scheme to choose the correct authentication scheme at runtime
        services
            .AddAuthentication(sharedOptions =>
            
                sharedOptions.DefaultScheme = "smart";
                sharedOptions.DefaultChallengeScheme = "smart";
            )
            .AddPolicyScheme("smart", "Authorization Bearer or OIDC", options =>
            
                options.ForwardDefaultSelector = context =>
                
                    var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
                    if (authHeader?.StartsWith("Bearer ") == true)
                    
                        return JwtBearerDefaults.AuthenticationScheme;
                    
                    return OpenIdConnectDefaults.AuthenticationScheme;
                ;
            )
            .AddJwtBearer(o =>
            
                o.Authority = Configuration["JWT:Authentication:Authority"];
                o.Audience = Configuration["JWT:Authentication:ClientId"];
                o.SaveToken = true;
            )
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddAzureAd(options => Configuration.Bind("AzureAd", options));

        services
            .AddMvc(config =>
            
                var policy = new AuthorizationPolicyBuilder()
                                 .RequireAuthenticatedUser()
                                 .Build();
                // Authentication is required by default
                config.Filters.Add(new AuthorizeFilter(policy));
                config.RespectBrowserAcceptHeader = true;
            );
            
            ...
            
            

编辑于 07/2019:我必须添加一个指向以下提案的链接,因为它也很有帮助:您可能不会像我一样在 AddAuthentication() 中使用参数,因为这会设置默认方案。一切都在这里得到很好的解释: Use multiple JWT Bearer Authentication。 我真的很喜欢这种另一种方法!

【讨论】:

我尝试了上面的代码,但收到错误Process is terminating due to ***Exception (DotNet Core 2.2)。如果提供了令牌,我想使用 JWT,否则 openid 连接。 这种方法还不错,发生的情况是两种身份验证机制总是被触发,实际上你引用的url以更好的方式面对这个,实际上我找不到一种方法来选择性地通过在 Authorize 属性中指定 Scheme 似乎被忽略了,我实现它的唯一方法是在 [Authorize] 中指定策略,并且在策略定义中指定 Scheme。 您确定两个处理程序都使用此配置运行吗?我已经尝试实现上述内容,并且仅在我的情况下运行相关的处理程序。另一方面,链接的解决方案每次都运行。奇怪的。也许是因为它们都是 JWT,而我用它来解决使用 Bearer 或 API 密钥的问题。 感谢@barbara.post! 芭芭拉,你的技术很好,即使有参数。 @john-reilly 在他的“使用 ASP.NET Core 的双启动身份验证”博文中解释了如何:blog.johnnyreilly.com/2020/03/…【参考方案2】:

浏览这些更改很困难,但我猜我做错了 .AddScheme。

不要使用AddScheme:它是为处理程序编写者设计的低级方法。

如何在 ASP.NET Core 2.0 中设置多个身份验证方案?

要注册 cookie 处理程序,只需执行以下操作:

public class Startup

    public void ConfigureServices(IServiceCollection services)
    
        services.AddAuthentication(options =>
        
            options.DefaultScheme = "myauth1";
        )

       .AddCookie("myauth1");
       .AddCookie("myauth2");
    

    public void Configure(IApplicationBuilder app)
    
        app.UseAuthentication();

        // ...
    

重要的是要注意,您不能像在 1.x 中那样注册多个默认方案(这个巨大重构的重点是避免同时拥有多个自动身份验证中间件)。

如果您绝对需要在 2.0 中模拟这种行为,您可以编写一个自定义中间件,手动调用 AuthenticateAsync() 并创建一个包含您需要的所有身份的 ClaimsPrincipal

public class Startup

    public void ConfigureServices(IServiceCollection services)
    
        services.AddAuthentication(options =>
        
            options.DefaultScheme = "myauth1";
        )

       .AddCookie("myauth1");
       .AddCookie("myauth2");
    

    public void Configure(IApplicationBuilder app)
    
        app.UseAuthentication();

        app.Use(async (context, next) =>
        
            var principal = new ClaimsPrincipal();

            var result1 = await context.AuthenticateAsync("myauth1");
            if (result1?.Principal != null)
            
                principal.AddIdentities(result1.Principal.Identities);
            

            var result2 = await context.AuthenticateAsync("myauth2");
            if (result2?.Principal != null)
            
                principal.AddIdentities(result2.Principal.Identities);
            

            context.User = principal;

            await next();
        );

        // ...
    

【讨论】:

我建立了一个干净的项目并使用了上面的代码,然后创建了登录方法(类似于我的问题中的代码),但它不起作用。 User.Identities 上只有一个身份,它是与默认值关联的身份。另外,奇怪的是,它的 IsAuthenticated 属性是假的。 如果没有看到完整的应用程序,很难说出问题所在。考虑更新您的问题以包含详细的 ASP.NET Core 日志。这肯定会有所帮助。 我在上面添加了代码......这就是整个应用程序。如果我调试,然后点击 /testy/in1 然后点击 /testy/in2,然后返回 /testy 并在 Index() 的第一行放置一个断点,只有一个标识。不确定您想查看哪些日志。 正如我所说,这是现在 2.0 中的预期行为,每个应用程序只能有一个默认处理程序(因此,每个请求只有一个身份)。考虑使用[Authorize(AuthenticationSchemes = "myauth1")][Authorize(AuthenticationSchemes = "myauth2")] 装饰您的操作,以便为每个操作选择正确的身份。 答案已更新以包含自定义中间件示例。【参考方案3】:

https://***.com/a/51897159/4425154 的解决方案有帮助。在提到的解决方案之上要考虑几个项目,

    确保您使用的是 .net core run-time 2.1 或更高版本

    如果您使用中间件,请确保您的授权策略如下所述

       services.AddMvc(options =>
        
            var defaultPolicy = new AuthorizationPolicyBuilder(new[]  CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme )
                      .RequireAuthenticatedUser()
                      .Build();
            options.Filters.Add(new AuthorizeFilter(defaultPolicy));
        )
    

【讨论】:

在 dotnet 核心上进行设置的方法多么复杂。天哪.. 谢谢你的回答。【参考方案4】:

如果有人需要解决方案,这就是我所做的:

services.AddMvc(options =>

            
     var defaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(IdentityServerAuthenticationDefaults.AuthenticationScheme, BasicAuthenticationDefaults.AuthenticationScheme)
         .RequireAuthenticatedUser()
         .Build();

      options.Filters.Add(new AuthorizeFilter(defaultPolicy));
);

services.AddAuthentication()
    .AddIdentityServerAuthentication(option config here)
    .AddBasicAuthentication(setting);

【讨论】:

【参考方案5】:
//******Startup=>ConfigureServices******

services.AddAuthentication(option =>

    option.DefaultScheme = "AdministratorAuth";
)
.AddCookie("AdministratorAuth", "AdministratorAuth", option =>

    option.Cookie.Name = "AdministratorAuth";
    option.LoginPath = new PathString("/AdminPanel/Login");
    option.ExpireTimeSpan = TimeSpan.FromMinutes(14400);
    option.AccessDeniedPath = "/Error/UnAuthorized";
    option.LogoutPath = "/Security/Logout";
)
.AddCookie("UsersAuth", "UsersAuth", option =>

    option.Cookie.Name = "UsersAuth";
    option.LoginPath = new PathString("/Security/LoginUser/");
    option.ExpireTimeSpan = TimeSpan.FromMinutes(144000);
    option.AccessDeniedPath = "/Error/UnAuthorized";
    option.LogoutPath = "/Security/LogoutUser";
);
    
//______________________________________________________________
    
//******Startup=> Configure******
    app.UseAuthentication();
    app.UseCookiePolicy();

//______________________________________________________________
    
//******Admin Login******
    var status = HttpContext.SignInAsync("AdministratorAuth", new ClaimsPrincipal(principal), properties)IsCompleted;
    
//******OtherUsers Login******
    var status = HttpContext.SignInAsync("UsersAuth", new ClaimsPrincipal(principal), properties)IsCompleted;
    
//______________________________________________________________
    
[Authorize(AuthenticationSchemes = "AdministratorAuth")]
public class DashboardController : BaseController



【讨论】:

【参考方案6】:

扩展@HotN 解决方案 如果将 Blazor 服务器 与 AddDefaultIdentity 和 Blazor Wasm JwtBearer

一起使用
    services.AddAuthentication(opt =>
    
        opt.DefaultAuthenticateScheme = "smart";
        opt.DefaultChallengeScheme = "smart";
    )
    .AddPolicyScheme("smart", "Authorization Bearer or OIDC", options =>
    
        options.ForwardDefaultSelector = context =>
        
            var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
            if (authHeader?.ToLower().StartsWith("bearer ") == true)
            
                return JwtBearerDefaults.AuthenticationScheme;
            
            return IdentityConstants.ApplicationScheme;
        ;
    )
    .AddCookie(cfg => cfg.SlidingExpiration = true)
    .AddJwtBearer(options =>
    
        options.TokenValidationParameters = new()
        
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,

            ValidIssuer = jwtSettings["ValidIssuer"],
            ValidAudience = jwtSettings["ValidAudience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"])),
        ;

    );

【讨论】:

以上是关于如何在 ASP.NET Core 2.0 中设置多个身份验证方案?的主要内容,如果未能解决你的问题,请参考以下文章