Авторизация 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")]