Нужна идея о том, как управлять ролями в моем приложении (ASP.NET MVC3)
Я разрабатываю какой-то веб-сайт, который представляет собой своего рода рабочее место в Интернете, будет несколько пользователей и некоторые текущие проекты по компьютерному программированию, и каждый пользователь может иметь несколько ролей, например, один конкретный пользователь может быть менеджером проекта для разработчика и разработчиком. для другого проекта. естественно, руководитель проекта имеет больше полномочий, чем разработчик проекта. мой вопрос, как это аккуратно в моем коде? Я собирался использовать свой собственный поставщик ролей и использовать с ним атрибут Authorize, но этого недостаточно, поскольку мне понадобится идентификатор проекта плюс идентификатор пользователя, чтобы найти роль пользователя в конкретном проекте.
7 ответов
Сначала вам нужно будет создать дополнительные таблицы для расширенного управления ролями, например projects
и там отношения с users
в контексте operations
, который может быть вашим controller's actions
,
Одним из способов является создание собственной таблицы для roles
, В этом случае вы будете использовать только сеть Asp membership users
, но все зависит от ваших требований.
Во-вторых, вы должны справиться с этим в MVC
На мой взгляд, лучший способ это реализовать через свой собственный Authorization
атрибут, и украсить действия вашего контроллера с вашим пользовательским атрибутом авторизации вместо [Authorization]
приписывать.
Это очень просто.
[CustomAuthorize]
//[Authorize]
public ActionResult GetProjectTasks(string projectname)
{
}
Для этого вы должны свойственный класс от FilterAttribute
а также должны реализовать IAuthorizationFilter
интерфейс.
public void OnAuthorization(AuthorizationContext filterContext)
{
HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var identity = new GenericIdentity(authTicket.Name, "Forms");
var principal = new GenericPrincipal(identity, new string[] { authTicket.UserData });
filterContext.HttpContext.User = principal;
}
var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var action = filterContext.ActionDescriptor.ActionName;
var user = filterContext.HttpContext.User;
var ip = filterContext.HttpContext.Request.UserHostAddress;
var isAccessAllowed = CustomAuthenticationLogic.IsAccessAllowed(controller, action, user, ip);
if (!isAccessAllowed)
{
// Code if user is authenticated
FormsAuthentication.RedirectToLoginPage();
}
}
В методе OnAuthorization
, вы можете получить всю информацию, которая вам может потребоваться в вашей пользовательской логике авторизации, например HttpContext
, Controller
название, Action
название. Вы должны просто вызвать свою собственную логику аутентификации из этого метода. Ваша пользовательская логика аутентификации может выглядеть следующим образом.
public class CustomAuthenticationLogic
{
public static bool IsAccessAllowed(string controller, string action, IPrincipal user, string ip)
{
//
// Your custom logic here
//
}
}
Некоторое время назад я провел небольшое исследование и могу вас заверить:
- Встроенные функции ASP.NET, скорее всего, не помогут (нет способа учесть такие вещи, как идентификатор проекта)
- Ролевая модель доступа является наиболее подходящей, есть разные способы ее реализации. AzMan, предложенный Rusted, на самом деле хорош, но управлять правилами, связанными с контекстом, может быть сложно. Например: пользователь A выполняет операцию B в проекте C, в то время как его, скажем, воскресенье. Посмотрите на Азмана.
- Смешивать ваши правила доступа с кодом очень плохо. Ваша модель безопасности не должна быть связана с тем, как работает приложение (ASP.NET MVC), так что это неверно:
var isAllowed = AccessControl.IsAccessAllowed(controller, action, user, ip);
это должно выглядеть так:
var isAllowed = AccessControl.IsAccessAllowed(user, operation, context);
тогда вы можете использовать его, когда захотите, в каждом действии или обернуть его как атрибут.
где операция "Вход в систему", "Ответ на сообщение", "Чтение тем" и т. д. контекст всегда важен, например, "идентификатор проекта", "день недели", "ip пользователя" и т. д.
Есть много вещей, которые могут быть написаны, например, перекрытие ролей, контекст и т. д. Вкратце: Google для "модели доступа на основе ролей.NET", вероятно, может быть проще написать небольшую специальную инфраструктуру безопасности. Сделайте так, чтобы он работал с пользователями, ролями, операциями и идентификатором проекта
Операции назначаются ролям, роли назначаются пользователям с определенным идентификатором проекта, вы можете жестко задавать операции и роли, поэтому в вашей БД будет только одно небольшое изменение: отображение пользователя на роли
Вы можете иметь groups
основанный на roles
,
Затем добавьте разных пользователей в определенные группы. Группы могут быть>
1) Admin Group
2) Developer Group
3) Project1-QA Group
4) Project2-Manager Group
Сохранить отображение [user - group]
а также [group - projects]
в зависимости от вашего дизайна базы данных.
Вы можете иметь столько ролей (групп) для одного пользователя, сколько захотите.
Я бы предложил вам создать кастом Authorize
фильтровать, расширяя встроенный AuthorizeAttribute
фильтр вместо реализации IAuthorizationFilter
интерфейс. Встроенный AuthorizeAttribute
выполняет много сантехнических работ, которые решают проблему с кешем и другие вещи, и если вы собираетесь реализовать интерфейс, вы должны выполнить всю эту работу.
Вы должны переопределить AuthorizeCore
метод и там у вас есть вся ваша логика проверки роли. user id
Вы должны хранить в сессии и project id
Вы должны понять это.
public override bool AuthorizeCore(HttpContextBase httpContext)
{
}
AuthorizeAttribute исходный код - http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/913d37658a44
Пример пользовательского атрибута авторизации: http://msdn.microsoft.com/en-us/library/ee707357(v=vs.91).aspx
Если у вас есть более сложные правила и недостаточно атрибутов, вы можете вычислить в своем контроллере, может ли пользователь получить доступ к некоторым функциям и добавить свойства в вашей модели представления, которые отражают доступ к этим функциям или его отсутствие.
Таким образом, ваше представление будет очень тонким, оно будет отображать вещи в зависимости от этих логических свойств ViewModel.
Таким образом, представляя, что ваш пользователь может только читать, у вас может быть свойство bool IsReadOnly, которое будет соответствовать в контроллере, в зависимости от правил авторизации, и которое будет использоваться в представлении, например, для создания меток вместо текстовых полей.
Мне нравится основная идея AzMan - концепция программирования против операций.
Операции - это очень детальные вещи, которые не должны перекрываться при использовании и определяются только разработчиком. Группируя операции по задачам и задачам по ролям, а затем сопоставляя принципалов (пользователей и группы) с ролями, вы получаете очень мощную модель для определения авторизации в вашем приложении. Поскольку вы программируете непосредственно для операций, ваш код не должен знать, какие роли имеет пользователь, и что может быть изменено администратором во время выполнения. Фактически, кто-то может определить совершенно другой набор ролей для использования операций, которые вы используете в своем коде, и вам вообще не нужно будет менять какой-либо код. Вот где настоящая сила.
Я не имею в виду "использовать AzMan в своем приложении" (но, возможно, вам следует попробовать). Это мощная модель, но она также сложна и, вероятно, излишня для простых вещей. Если у вас есть только одна или две роли и защищаемые ими операции не пересекаются или вряд ли изменятся, то это, вероятно, не оправдано.
Очень простой подход - для управления доступом на уровне сайта вы можете добавить столбец INT в пользовательскую таблицу и отобразить каждый бит этого INT в [flags]
Enum - например [Flags] enum Access { UpdateProjects, AddProjects }
,
Для управления доступом к проекту создайте таблицу с именем, например, ProjectAccessControl, с тремя столбцами: ProjectID (внешний ключ к таблице Project), UserID (внешний ключ к таблице User) и Role (INT). Столбец Role представляет собой INT, и каждый его бит должен обозначать свой логический флаг (как в предыдущем примере, вы можете отобразить это на enum в C#) и сказать, что если включен первый бит, то у пользователя есть права на обновление описания, если второй бит включен, пользователь может изменить расписание и так далее.
[Flags]
enum ProjectAccessRole
{
UpdateDescription,
ChangeSchedule,
etc...
}
В коде вы можете проверить, имеет ли роль пользователя право обновлять расписание следующим образом:
if( (intUserRole & ProjectAccessRole.ChangeSchedule)
== ProjectAccessRole.ChangeSchedule)
{
/*user has right*/
}
Затем вы можете заключить эту проверку в простую функцию, которая принимает два параметра: 1) роль, которая должна быть проверена, если она имеет 2) роль. Тогда вы просто позвоните HasRights(intUserRole, ProjectAccessRole.ChangeSchedule);
,