如何在 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 中设置多个身份验证方案?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 2.0 中的应用程序变量

如何在 ASP.NET Core 中设置 cookie validateInterval?

如何在 ASP.NET Core 2.0 中根据路由配置服务身份验证

如何在 ASP.NET Core 中设置默认区域?

如何在 ASP.NET Core 2.0 中预编译视图?

如何在 ASP.NET Core 2.0 Razor 页面中填充下拉列表