Авторизация MVC
Я пытаюсь реализовать механизм авторизации роли, который проверяет роли текущего зарегистрированного пользователя, если пользователь находится в правильной роли, он разрешен, иначе отображает представление ошибки.
Проблема заключается в том, что когда пользователь пытается получить доступ к указанному ниже методу в контроллере, он попадает в класс RoleAuthorizationAttribute и получает подтверждение, но тогда метод в контроллере не выполняется.
Примечание: у пользователя есть роль Клиента
Метод контроллера
[RoleAuthorization(Roles = "Client, Adminsitrator")]
public ActionResult addToCart(int ProductID, string Quantity)
{
tempShoppingCart t = new tempShoppingCart();
t.ProductID = ProductID;
t.Username = User.Identity.Name;
t.Quantity = Convert.ToInt16(Quantity);
new OrdersService.OrdersClient().addToCart(t);
ViewData["numberOfItemsInShoppingCart"] = new OrdersService.OrdersClient().getNoOfItemsInShoppingCart(User.Identity.Name);
ViewData["totalPriceInSC"] = new OrdersService.OrdersClient().getTotalPriceOfItemsInSC(User.Identity.Name);
return PartialView("quickShoppingCart", "Orders");
}
Класс проверки подлинности роли
[System.AttributeUsage(System.AttributeTargets.All,AllowMultiple = false, Inherited = true)]
public sealed class RoleAuthorizationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
List<String> requiredRoles = Roles.Split(Convert.ToChar(",")).ToList();
List<Role> allRoles = new UsersService.UsersClient().GetUserRoles(filterContext.HttpContext.User.Identity.Name).ToList();
bool Match = false;
foreach (String s in requiredRoles)
{
foreach (Role r in allRoles)
{
string rName = r.RoleName.Trim().ToString();
string sName = s.Trim();
if (rName == sName)
{
Match = true;
}
}
}
if (!Match)
{
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
base.OnAuthorization(filterContext);
}
}
Не могли бы вы рассказать мне, что я делаю неправильно?
Ответы
Ответ 1
Поскольку у меня были роли пользователей в базе данных, которые я должен был проверять на базе базы данных, поэтому я включил этот метод в файл global.asax
protected void Application_AuthenticateRequest(object sender, EventArgs args)
{
if (Context.User != null)
{
IEnumerable<Role> roles = new UsersService.UsersClient().GetUserRoles(
Context.User.Identity.Name);
string[] rolesArray = new string[roles.Count()];
for (int i = 0; i < roles.Count(); i++)
{
rolesArray[i] = roles.ElementAt(i).RoleName;
}
GenericPrincipal gp = new GenericPrincipal(Context.User.Identity, rolesArray);
Context.User = gp;
}
}
Тогда я мог бы использовать обычный
[Authorize(Roles = "Client, Administrator")]
В верхней части методов actionResult в контроллерах
Это сработало.
Ответ 2
Если вы используете MVC 5, вам нужно включить ленивую загрузку в свой DbContext, поместив следующую строку в вашу инициализацию DbContext.
this.Configuration.LazyLoadingEnabled = true;
В проекте MVC 5 по умолчанию вы добавите его в файл ApplicationDbContext.cs.
Я не уверен, что это особенно важно для MVC 5, для Identity 2.0 или для других версий. Я использую эту настройку и позволяю ленивой загрузке создавать все схемы ролей по умолчанию. Подробнее см. fooobar.com/questions/411155/....
Кроме того, если вы используете схему разрешений по умолчанию для ASP.NET Identity 2.0, вам не нужно реализовывать Application_AuthenticateRequest, как упоминал Даррен. Но если вы используете настраиваемые таблицы авторизации, тогда вы также должны реализовать его.
Ответ 3
Ваш оригинальный код был близок, но проблема здесь:
base.OnAuthorization(filterContext);
Безусловное обращение к базовому классу означает, что вам требуется, чтобы декорированные роли были найдены в BOTH UserService и встроенном ролевом провайдере. Если поставщик ролей не настроен на возврат одного и того же набора ролей (чего не было бы, если для вас недостаточно авторизованного атрибута AuthorizeAttribute), то это, очевидно, приведет к тому, что тест авторизации всегда возвращает false.
Вместо этого вы можете добавить отдельное свойство к производному атрибуту, например
public string RemoteRoles { get; set; }
и замените
List<String> requiredRoles = Roles.Split(Convert.ToChar(",")).ToList();
с:
List<String> requiredRoles = RemoteRoles.Split(Convert.ToChar(",")).ToList();
И украсьте свой контроллер следующим образом:
[RoleAuthorization (RemoteRoles = "Client, Administrator")]