Как настроить несколько схем авторизации в ASP.NET Core 2.0?

Я пытаюсь перенести свои данные аутентификации на 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?}");
            });
        }
    }
}

6 ответов

Решение

Навигация по этим изменениям была трудной, но я предполагаю, что я делаю.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();
        });

        // ...
    }
}

Другая возможность состоит в том, чтобы определить во время выполнения, какую схему политики аутентификации выбрать, у меня был случай, когда у меня мог быть заголовок токена носителя 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;
            });

            ...

            }

Решение /questions/27454796/kak-nastroit-neskolko-shem-avtorizatsii-v-aspnet-core-20/27454813#27454813 помогает. Пара вопросов, которые следует рассмотреть в дополнение к упомянутому решению,

  1. Убедитесь, что вы используете.net core run-time 2.1 или выше
  2. Убедитесь, что вы используете политику авторизации, как указано ниже, если вы используете промежуточное ПО

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

Расширьте решение @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"])),
        };

    });

В случае, если кому-то нужно решение, вот что я сделал:

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);
//******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
{

}
Другие вопросы по тегам