Как настроить несколько схем авторизации в 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 помогает. Пара вопросов, которые следует рассмотреть в дополнение к упомянутому решению,
- Убедитесь, что вы используете.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)); })
Расширьте решение @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
{
}