Ответ 1
Вам нужно будет немного поиграть с картой, поскольку ваша глобальная политика более ограничительна, чем та, которую вы хотите применить к определенным контроллерам и действиям:
- По умолчанию только пользователи Admin могут получить доступ к вашему приложению
- Конкретным ролям также будет предоставлен доступ к некоторым контроллерам (например, к Менеджерам пользователей, обращающимся к
UsersController
)
Как вы уже отмечали, наличие глобального фильтра означает, что только администраторы получат доступ к контроллеру. Когда вы добавите дополнительный атрибут в UsersController
, будут доступны только те пользователи, которые и Admin и UserManager.
Можно использовать аналогичный подход к MVC 5, но он работает по-другому.
- В MVC 6 атрибут
[Authorize]
не содержит логики авторизации. - Вместо
AuthorizeFilter
используется методOnAuthorizeAsync
, вызывающий службу авторизации, чтобы убедиться, что политики выполнены. - Конкретный
IApplicationModelProvider
используется для добавленияAuthorizeFilter
для каждого контроллера и действия с атрибутом[Authorize]
.
Один из вариантов может состоять в том, чтобы воссоздать ваш IsAdminOrAuthorizeAttribute
, но на этот раз как AuthorizeFilter
, который вы затем добавите в качестве глобального фильтра:
public class IsAdminOrAuthorizeFilter : AuthorizeFilter
{
public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext context)
{
// If there is another authorize filter, do nothing
if (context.Filters.Any(item => item is IAsyncAuthorizationFilter && item != this))
{
return Task.FromResult(0);
}
//Otherwise apply this policy
return base.OnAuthorizationAsync(context);
}
}
services.AddMvc(opts =>
{
opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
});
Это применит ваш глобальный фильтр только тогда, когда контроллер/действие не имеет определенного атрибута [Authorize]
.
Вы также можете избежать использования глобального фильтра, введя себя в процесс, который генерирует фильтры для каждого контроллера и действия. Вы можете добавить свой собственный IApplicationModelProvider
или свой собственный IApplicationModelConvention
. Оба позволяют добавлять/удалять определенные фильтры контроллеров и действий.
Например, вы можете определить политику авторизации по умолчанию и дополнительные конкретные политики:
services.AddAuthorization(opts =>
{
opts.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole("admin").Build();
opts.AddPolicy("Users", policy => policy.RequireAuthenticatedUser().RequireRole("admin", "users"));
});
Затем вы можете создать новый IApplicatioModelProvider
, который добавит политику по умолчанию для каждого контроллера, у которого нет своего собственного атрибута [Authorize]
(соглашение с приложением было бы очень похоже и, вероятно, более согласовано с тем, как структура предназначенный для расширения. Я просто использовал существующий AuthorizationApplicationModelProvider
в качестве руководства):
public class OverridableDefaultAuthorizationApplicationModelProvider : IApplicationModelProvider
{
private readonly AuthorizationOptions _authorizationOptions;
public OverridableDefaultAuthorizationApplicationModelProvider(IOptions<AuthorizationOptions> authorizationOptionsAccessor)
{
_authorizationOptions = authorizationOptionsAccessor.Value;
}
public int Order
{
//It will be executed after AuthorizationApplicationModelProvider, which has order -990
get { return 0; }
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
foreach (var controllerModel in context.Result.Controllers)
{
if (controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().FirstOrDefault() == null)
{
//default policy only used when there is no authorize filter in the controller
controllerModel.Filters.Add(new AuthorizeFilter(_authorizationOptions.DefaultPolicy));
}
}
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
//empty
}
}
//Register in Startup.ConfigureServices
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, OverridableDefaultAuthorizationApplicationModelProvider>());
При этом на этих двух контроллерах будет использоваться политика по умолчанию:
public class FooController : Controller
[Authorize]
public class BarController : Controller
И определенная политика пользователей будет использоваться здесь:
[Authorize(Policy = "Users")]
public class UsersController : Controller
Обратите внимание, что вам все равно нужно добавить роль администратора в каждую политику, но по крайней мере все ваши политики будут объявлены одним методом запуска. Вероятно, вы могли бы создать свои собственные методы для создания политик, которые всегда будут добавлять роль администратора.