#c# #authorization #identityserver4 #idp
#c# #авторизация #identityserver4 #idp
Вопрос:
Я пытаюсь выбрать схему авторизации OpenID на основе параметра запроса.
Я добавил две схемы OpenIdConnect:
.AddOpenIdConnect("abc", abcHandler)
.AddOpenIdConnect("def", defHandler);
Вначале я переопределил все промежуточное программное обеспечение авторизации и бросил вызов:
if (authorizeResult.Challenged)
{
if (valueFromQueryString.Contains("abc"))
{
await context.ChallengeAsync("abc");
}
else if (valueFromQueryString.Contains("def"))
{
await context.ChallengeAsync("def");
}
}
Но потом я заметил, что есть некоторые политики, поэтому попытался их использовать. Я создал свое требование, а затем добавил две политики с подобными схемами:
services.AddAuthorization(options =>
{
options.AddPolicy("abc", policy =>
{
policy.AuthenticationSchemes.Add("abc");
policy.Requirements.Add(new TenantSpecificRequirement("abc"));
policy.RequireAuthenticatedUser();
});
options.AddPolicy("def", policy =>
{
policy.AuthenticationSchemes.Add("def");
policy.Requirements.Add(new TenantSpecificRequirement("def"));
policy.RequireAuthenticatedUser();
});
});
и я понял, что политика должна быть привязана к контроллеру следующим образом :
[Authorize(Policy = "abc")]
[Authorize(Policy = "def")]
это совершенно не то, что мне нужно. Сейчас лучше всего было бы назначить политику контроллеру динамически на основе URL, но, похоже, это невозможно, поскольку фильтры контроллеров не добавляются при запуске приложения.
Так есть ли другой способ?
Ответ №1:
Хорошо, я нашел решение, переопределив AuthenticationSchemeProvider
public class SubdomainAuthenticationSchemeProvider : AuthenticationSchemeProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomAuthenticationSchemeProvider(
IHttpContextAccessor httpContextAccessor,
IOptions<AuthenticationOptions> options)
: base(options)
{
this._httpContextAccessor = httpContextAccessor;
}
private Task<AuthenticationScheme> GetRequestSchemeAsync()
{
// Get the subdomain for the host the request came from. Assumes no WWW in the string.
var subdomainName = _httpContextAccessor.HttpContext.Request.Host.Host.Split(new char[] { '.' })[0];
return GetSchemeAsync(subdomainName);
}
public override async Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync() => await GetRequestSchemeAsync() ;
public override async Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultChallengeSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultForbidSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultForbidSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultSignInSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultSignInSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultSignOutSchemeAsync();
}
После этого добавьте его в метод ConfigureServices
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthenticationSchemeProvider, SubdomainAuthenticationSchemeProvider>();