Управление ролью и разрешениями ASP.NET MVC3 → С назначением разрешения времени выполнения
ASP.NET MVC позволяет пользователям назначать разрешения для функциональности (т.е. действия) во время разработки, например.
[Authorize(Roles = "Administrator,ContentEditor")]
public ActionResult Foo()
{
return View();
}
Чтобы проверить разрешение, можно использовать следующий оператор в представлении (Razor):
@if (User.IsInRole("ContentEditor"))
{
<div>This will be visible only to users in the ContentEditor role.</div>
}
Проблема с этим подходом заключается в том, что все разрешения должны быть установлены и назначены как атрибуты во время разработки. (Атрибуты скомпилированы в DLL, поэтому я в настоящее время не знаю о механизме применения атрибутов (для разрешения дополнительных разрешений), таких как [Authorize (Roles = "Administrator, ContentEditor" )] во время выполнения.
В нашем прецеденте клиент должен иметь возможность изменять, какие у пользователей есть разрешения после развертывания.
Например, клиент может пожелать разрешить пользователю в роли ContentEditor
редактировать содержимое определенного типа. Возможно, пользователю не разрешили изменять значения таблицы поиска, но теперь клиент хочет разрешить это, не предоставив пользователю все разрешения в следующей более высокой роли. Вместо этого клиент просто хочет изменить разрешения, доступные для текущей роли пользователя.
Какие существуют варианты стратегий, позволяющих определять разрешения для MVC-контроллеров/представлений/действий за пределами атрибутов (как в базе данных) и оцениваться и применяться во время выполнения?
Если возможно, мы очень хотели бы придерживаться максимально возможного уровня возможностей для ASP.NET-членства и роли поставщика, чтобы мы могли продолжать использовать другие преимущества, которые он предоставляет.
Заранее благодарю за любые идеи или идеи.
Ответы
Ответ 1
Какие существуют варианты стратегий для разрешения разрешений на MVC Контроллеры/представления/действия, которые должны быть определены вне атрибутов (как в базы данных) и оценивается и применяется во время выполнения?
Пользовательский атрибут Authorize является одной из возможностей для достижения этой цели:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
Roles = ... go ahead and fetch those roles dynamically from wherever they are stored
return base.AuthorizeCore(httpContext);
}
}
а затем:
[MyAuthorize]
public ActionResult Foo()
{
return View();
}
Ответ 2
Как я ленив, я не мог потрудиться, катив мой собственный атрибут, и использовал FluentSecurity. В дополнение к возможности применения правил во время выполнения он позволяет настраивать способ проверки членства в роли. В моем случае у меня есть параметр файла конфигурации для каждой роли, а затем я реализую что-то вроде следующего:
// Map application roles to configuration settings
private static readonly Dictionary<ApplicationRole, string>
RoleToConfigurationMapper = new Dictionary<ApplicationRole, string>
{
{ ApplicationRole.ExceptionLogViewer, "ExceptionLogViewerGroups" }
};
роли приложения затем применяются так
SecurityConfigurator.Configure(
configuration =>
{
configuration.GetAuthenticationStatusFrom(() =>
HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(() =>
GetApplicationRolesForPrincipal(HttpContext.Current.User));
configuration.ForAllControllers().DenyAnonymousAccess();
configuration.For<Areas.Administration.Controllers.LogViewerController>()
.RequireRole(ApplicationRole.ExceptionLogViewer);
});
filters.Add(new HandleSecurityAttribute());
а затем проверка выполняется
public static object[] GetApplicationRolesForPrincipal(IPrincipal principal)
{
if (principal == null)
{
return new object[0];
}
List<object> roles = new List<object>();
foreach (KeyValuePair<ApplicationRole, string> configurationMap in
RoleToConfigurationMapper)
{
string mappedRoles = (string)Properties.Settings.Default[configurationMap.Value];
if (string.IsNullOrEmpty(mappedRoles))
{
continue;
}
string[] individualRoles = mappedRoles.Split(',');
foreach (string indvidualRole in individualRoles)
{
if (!roles.Contains(configurationMap.Key) && principal.IsInRole(indvidualRole))
{
roles.Add(configurationMap.Key);
if (!roles.Contains(ApplicationRole.AnyAdministrationFunction))
{
roles.Add(ApplicationRole.AnyAdministrationFunction);
}
}
}
}
return roles.ToArray();
}
Конечно, вы могли бы вытащить роли из базы данных. Самое приятное в том, что я могу применять разные правила во время разработки, плюс кто-то уже сделал для меня тяжелую работу!
Ответ 3
Вы также можете рассмотреть возможность обеспечения безопасности на основе задач/действий и динамического назначения разрешений для выполнения этих задач для разных групп.
http://lostechies.com/derickbailey/2011/05/24/dont-do-role-based-authorization-checks-do-activity-based-checks/
Вам нужно будет немного убрать провайдера, чтобы работать с ним, но можно оставаться в очереди с авторизацией .net
http://www.lhotka.net/weblog/PermissionbasedAuthorizationVsRolebasedAuthorization.aspx
Ответ 4
Если вам необходимо выполнить авторизацию на основе метода или контроллера (запретить доступ ко всему методу или контроллеру), вы можете переопределить OnAuthorization в базе контроллера и выполнить свою авторизацию. Затем вы можете создать таблицу для поиска того, какие разрешения назначены этому контроллеру/методу, и оттуда.
Вы также можете создать собственный глобальный фильтр, который очень похож.
Еще один вариант, используя ваш второй подход, - сказать что-то вроде этого:
@if (User.IsInRole(Model.MethodRoles))
{
<div>This will be visible only to users in the ContentEditor role.</div>
}
И затем в вашем контроллере заполните MethodRoles ролью, назначенной этому методу.