Как мы можем установить авторизацию для всей области в ASP.NET MVC?

У меня есть область администратора, и я хочу, чтобы в нее входили только администраторы. Я рассмотрел добавление атрибута Authorized для каждого контроллера в области администратора. Разве нет элегантного решения или эта функция отсутствует в самой структуре?

РЕДАКТИРОВАТЬ: Извините, я должен был упомянуть об этом раньше. Я использую пользовательский AuthorizedAttribute, полученный из AuthorizeAttribute.

8 ответов

Решение

Безопасность на основе Web.config почти никогда не должна использоваться в приложении MVC. Причина этого заключается в том, что несколько URL-адресов могут потенциально попасть на контроллер, и размещение этих проверок в Web.config неизменно что-то пропускает. Помните - контроллеры не связаны с областями, маршруты связаны с областями. Фабрика контроллеров MVC с радостью будет обслуживать контроллеры из папки Areas/ для запросов вне зоны, если нет конфликта.

Например, используя структуру проекта по умолчанию, добавив область администратора с AdminDefaultController, вы можете подключить этот контроллер через /Admin/AdminDefault/Index и /AdminDefault/Index.

Единственное поддерживаемое решение - поместить ваш атрибут в базовый класс контроллера и обеспечить, чтобы каждый контроллер в пределах подклассов области являлся базовым классом.

Я только что исследовал эту же проблему. Поскольку невозможно защитить контроллеры на основе областей, на ум приходит более простой вариант.

Создайте определение базового контроллера для каждой области, которая переопределяет контроллер, и добавьте к нему требования безопасности. Тогда вам просто нужно убедиться, что каждый контроллер в области переопределяет AreaController вместо Controller. Например:

/// <summary>
/// Base controller for all Admin area
/// </summary>
[Authorize(Roles = "Admin")]
public abstract class AdminController : Controller { }

Это все еще требует, чтобы вы получили каждый контроллер в области администратора из этой базы,

public class HomeController : AdminController
{
    // .. actions
}

но, по крайней мере, у вас есть единственная точка, в которой вы определяете безопасность области.

Я только начал с этого... но пока это работает довольно хорошо для меня.

Я создаю собственный класс AuthorizeAttribute и добавляю его в функцию RegisterGlobalFilters.

В CustomAuthorizeAttribute я проверяю различные условия в зависимости от области, в которой он находится.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomAuthorizeAttribute());
        filters.Add(new HandleErrorAttribute());
    }
}

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var routeData = httpContext.Request.RequestContext.RouteData;
        var controller = routeData.GetRequiredString("controller");
        var action = routeData.GetRequiredString("action");
        var area = routeData.DataTokens["area"];
        var user = httpContext.User;
        if (area != null && area.ToString() == "Customer")
        {
            if (!user.Identity.IsAuthenticated)
                return false;
        }
        else if (area != null && area.ToString() == "Admin")
        {
            if (!user.Identity.IsAuthenticated)
                return false;
            if (!user.IsInRole("Admin"))
                return false;
        }
        return true;
    }
}

Если весь ваш код администратора находится в одном контроллере, то добавьте Authorize ко всему классу.

[Authorize]
public class AdminController : Controller
{
     .......
}

Принятый в настоящее время ответ не является наиболее безопасным решением, поскольку он требует от разработчика всегда помнить о наследовании этого нового базового класса для любых новых контроллеров или действий ("черный список"; предоставление пользователям доступа ко всему, если действие не ограничено вручную). Это особенно вызывает проблемы, когда к проекту знакомятся новые разработчики, незнакомые с вашими ритуалами. Легко забыть унаследовать надлежащий класс контроллера, если это сделать таким образом, особенно после того, как вы отвлеклись от проекта на недели, месяцы или годы. Если разработчик забывает наследовать, не очевидно, что в проекте есть уязвимость.

Более безопасное решение этой проблемы - запретить доступ ко всем запросам, а затем украсить каждое действие ролями, которым разрешен доступ к действиям ("белый список"; запрещен доступ всем пользователям, если это не разрешено вручную). Теперь, если разработчик забудет внести в белый список правильную авторизацию, пользователи сообщат вам об этом, и это так же просто, как посмотреть на другие контроллеры для напоминания о том, как предоставить надлежащий доступ. Тем не менее, по крайней мере, нет серьезной уязвимости безопасности.

В файле App_Start/FilterConfig.cs измените класс FilterConfig:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        ...

        //Deny access to all controllers and actions so that only logged in Administrators can access them by default
        filters.Add(new System.Web.Mvc.AuthorizeAttribute() { Roles = "Administrator" });
    }

Это делает все действия недоступными, если пользователь не вошел в систему как администратор. Затем для каждого действия, к которому вы хотите, чтобы у другого авторизованного пользователя был доступ, вы просто украшаете его [OverrideAuthorization] а также [Authorize],

В вашей бизнес-логике это позволяет вам использовать атрибут Authorize различными способами, не беспокоясь о том, что неавторизованные пользователи могут получить доступ к каким-либо функциям. Ниже приведены некоторые примеры.

Пример 1 - Доступ разрешен только зарегистрированным пользователям с правами администратора и диспетчера. Index() Методы Get и Post.

public class MarkupCalculatorController : Controller //Just continue using the default Controller class.
{
    // GET: MarkupCalculator
    [OverrideAuthorization]
    [Authorize(Roles = "Administrator,Dispatcher")]
    public ActionResult Index()
    {
        //Business logic here.

        return View(...);
    }

    // POST: DeliveryFeeCalculator
    [HttpPost]
    [ValidateAntiForgeryToken]
    [OverrideAuthorization]
    [Authorize(Roles = "Administrator,Dispatcher")]
    public ActionResult Index([Bind(Include = "Price,MarkedupPrice")] MarkupCalculatorVM markupCalculatorVM)
    {
        //Business logic here.

        return View(...);
    }
}

Пример 2 - Только авторизованным пользователям будет разрешен доступ к контроллеру дома. Index() метод.

public class HomeController : Controller
{
    [OverrideAuthorization]
    [Authorize] //Allow all authorized (logged in) users to use this action
    public ActionResult Index()
    {
        return View();
    }

}

Пример 3 - Неаутентифицированным пользователям (т.е. анонимным пользователям) может быть разрешен доступ к методам с помощью [AllowAnonymous] приписывать. Это также автоматически переопределяет глобальный фильтр без необходимости [OverrideAuthorization] приписывать.

    // GET: /Account/Login
    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }

    //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        ...
    }

Пример 4. Только администраторам будет разрешен доступ к методам, в которых отсутствует [Authorize] приписывать.

public class LocationsController : Controller
{

    // GET: Locations
    public ActionResult Index()
    {
        //Business logic here.
        return View(...);
    }
}

Некоторые заметки.

Вы должны использовать [OverrideAuthorization] атрибут, если вы хотите ограничить доступ к определенному действию для определенных ролей. В противном случае [Authorize] Свойства атрибута будут игнорироваться, и будет разрешена только роль по умолчанию (администратор в моем примере), даже если вы указываете другие роли (например, Диспетчер и т. д.) из-за глобального фильтра. Любые неавторизованные пользователи будут перенаправлены на экран входа в систему.

С использованием [OverrideAuthorization] Атрибут заставляет действие игнорировать установленный вами глобальный фильтр. Поэтому вы должны повторно [Authorize] атрибут, когда вы используете переопределение, чтобы действие оставалось безопасным.

Относительно целых областей и контроллеров

Для ограничения по областям, как вы просите, поставьте [OverrideAuthorization] а также [Authorize] атрибуты на контроллере вместо отдельных действий.

Используя название области и просто косую черту с AuthorizeAreaFolder в startup.cs у меня работал:

services.AddRazorPages()
        .AddRazorPagesOptions(options => options.Conventions.AuthorizeAreaFolder("Admin", "/"))
        .WithRazorPagesAtContentRoot();
      builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Administrator"));
});
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeAreaFolder("Identity","/Account/Manage", "AdminPolicy");
});

.. очень грубо я верю, что вы хотите что-то подобное?

Быстрое и грязное управление ролями

[Authorize(Roles = "Admins")]
public ActionResult Register()
{
  ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
  ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
  return View();
}
Другие вопросы по тегам