如何在 ASP.NET MVC 中设置整个区域的授权?

发布于 2024-08-22 10:04:50 字数 159 浏览 10 评论 0原文

我有一个管理区域,我希望只有管理员才能进入该区域。我考虑向管理区域中的每个控制器添加授权属性。是否有一个优雅的解决方案或者框架本身不存在此功能?

编辑: 抱歉,我应该早点提到这一点。我正在使用从 AuthorizeAttribute 派生的自定义 AuthorizedAttribute。

I've an Admin area and I want only Admins to enter the area. I considered adding the Authorized attribute to every controller in the Admin area. Isn't there an elegant solution or is this feature not there in the framework itself?

EDIT:
I'm sorry, I should to have mentioned this before. I'm using a custom AuthorizedAttribute derived from AuthorizeAttribute.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

简单 2024-08-29 10:04:50

基于 Web.config 的安全性几乎永远应该用在 MVC 应用程序中。原因是多个 URL 可能会访问一个控制器,而将这些检查放在 Web.config 中总是会遗漏一些内容。请记住 - 控制器与区域无关,路线与区域相关联。如果不存在冲突,MVC 控制器工厂将乐意为非区域请求的 Areas/ 文件夹中的控制器提供服务。

例如,使用默认的项目结构,添加带有 AdminDefaultController 的管理区域,您可以通过 /Admin/AdminDefault/Index /AdminDefault/Index 访问此控制器。

唯一支持的解决方案是将属性放在控制器基类上,并确保区域内的每个控制器都是该基类的子类。

Web.config-based security should almost never be used in an MVC application. The reason for this is that multiple URLs can potentially hit a controller, and putting these checks in Web.config invariably misses something. Remember - controllers are not associated with areas, routes are associated with areas. The MVC controller factory will happily serve controllers from the Areas/ folder for non-area requests if there's no conflict.

For example, using the default project structure, adding an Admin area with an AdminDefaultController, you can hit this controller via /Admin/AdminDefault/Index and /AdminDefault/Index.

The only supported solution is to put your attribute on a controller base class and to ensure that each controller within the area subclasses that base class.

微凉徒眸意 2024-08-29 10:04:50

我刚刚在调查同样的问题。由于不可能基于区域来保护控制器,因此我想到了一个更简单的选项。

为覆盖控制器的每个区域创建一个基本控制器定义,并向其添加安全要求。然后你只需要确保该区域中的每个控制器都覆盖 AreaController 而不是 Controller 。例如:

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

它仍然要求您从该基础派生管理区域中的每个控制器,

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

但至少您有一个定义该区域安全性的点。

I have just been investigating this same issue. Since it is not possible to secure controllers based on areas, a simpler option comes to mind.

Create a base controller definition for each area that overrides Controller, and add the security require to this. Then you just have to ensure each controller in the area overrides AreaController instead of Controller. For example:

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

It does still require that you derive each controller in the Admin area from this base,

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

but at least you have a single point where you define the security for the area.

影子是时光的心 2024-08-29 10:04:50

我刚刚开始做这个......但到目前为止这对我来说效果很好。

我创建一个自定义 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;
    }
}

I just started on this... but so far this is working pretty good for me.

I create a custom AuthorizeAttribute class and add this in the RegisterGlobalFilters function.

In CustomAuthorizeAttribute I check for various conditions based on the area it is in.

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;
    }
}
吻风 2024-08-29 10:04:50

如果您的所有管理代码都在一个控制器中,则将 Authorize 添加到整个类。

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

If all of your admin code is in one controller then add Authorize to the entire class.

[Authorize]
public class AdminController : Controller
{
     .......
}
記憶穿過時間隧道 2024-08-29 10:04:50

当前接受的答案不是最安全的解决方案,因为它要求开发人员始终记住为任何新控制器或操作继承该新基类(“黑名单”;允许用户访问所有内容,除非操作是手动限制的)。当不熟悉您的惯例的新开发人员被引入项目时,这尤其会导致问题。如果这样做的话,很容易忘记继承正确的控制器类,尤其是在您将注意力从项目上移开几周、几个月或几年之后。如果开发人员忘记继承,那么项目中存在安全漏洞并不明显。

解决此问题的更安全的解决方案是拒绝对所有请求的访问,然后使用允许访问操作的角色来装饰每个操作(“白名单”;除非手动允许,否则阻止所有用户的访问) 。现在,如果开发人员忘记将正确的授权列入白名单,用户会让您知道,这就像查看其他控制器以获取有关如何提供正确访问权限的提醒一样简单。不过,至少没有出现重大安全漏洞。

在 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] 对其进行修饰即可。

在您的业务逻辑中,这允许您以多种方式使用授权属性,而无需担心未经授权的用户访问任何功能。以下是一些示例。

示例 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 - 只有经过身份验证的用户才可以访问 Home 控制器的 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] 属性放在控制器上的个人行动。

The currently accepted answer is not the most secure solution because it requires the developer to always remember to inherit that new base class for any new controllers or actions ("blacklisting"; allowing users access to everything unless an action is manually restricted). This especially causes problems when new developers, unacquainted with your rituals, are introduced to the project. It is easy to forget to inherit the proper controller class if done that way, especially after having taken your eyes off the project for weeks, months, or years. If a developer forgets to inherit, it isn't obvious that there is a security vulnerability in the project.

A more secure solution to this problem is to deny access to all requests, then decorate each action with the roles that are allowed access to the actions ("whitelisting"; preventing access to all users unless manually allowed). Now if a developer forgets to whitelist the proper authorization, the users will let you know and it's as simple as looking at other controllers for a reminder about how to give proper access. However, at least there is no major security vulnerability.

In App_Start/FilterConfig.cs file, modify the FilterConfig class:

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" });
    }

This makes all actions inaccessible unless the user is logged in as an Administrator. Then for each action that you want a different authorized user to have access to, you simply decorate it with [OverrideAuthorization] and [Authorize].

In your business logic, this allows you to use the Authorize attribute in a variety of ways without ever needing to worry about unauthorized users from accessing any functionality. Below are some examples.

Example 1 - Only logged in Administrator and Dispatcher users will be allowed to access Index() Get and Post methods.

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(...);
    }
}

Example 2 - Only authenticated users will be allowed to access the Home controller's Index() method.

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

}

Example 3 - Unauthenticated users (i.e. anonymous users) can be allowed to access methods by using the [AllowAnonymous] attribute. This also automatically overrides the global filter without needing the [OverrideAuthorization] attribute.

    // 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)
    {
        ...
    }

Example 4 - Only admins will be allowed access to methods that lack the [Authorize] attribute.

public class LocationsController : Controller
{

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

Some notes.

You must use the [OverrideAuthorization] attribute if you want to limit the access to a particular action to specific roles. Otherwise, the [Authorize] attribute properties will be ignored and only the default role (Administrator in my example) will be allowed, even if you specify other roles (e.g. Dispatcher, etc.) because of the global filter. Any unauthorized users will be redirected to the login screen.

Using the [OverrideAuthorization] attribute causes the action to ignore the global filter you set. Therefore, you must reapply the [Authorize] attribute whenever you use the override so that the action remains secure.

Regarding whole areas and controllers

To restrict by areas, as you are asking, put the [OverrideAuthorization] and [Authorize] attributes on the controller instead of the individual actions.

狂之美人 2024-08-29 10:04:50

在startup.cs中使用区域名称和带有AuthorizeAreaFolder的斜杠对我有用:

services.AddRazorPages()
        .AddRazorPagesOptions(options => options.Conventions.AuthorizeAreaFolder("Admin", "/"))
        .WithRazorPagesAtContentRoot();

Using the area name and just a slash with AuthorizeAreaFolder in startup.cs worked for me:

services.AddRazorPages()
        .AddRazorPagesOptions(options => options.Conventions.AuthorizeAreaFolder("Admin", "/"))
        .WithRazorPagesAtContentRoot();
归属感 2024-08-29 10:04:50
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Administrator"));
});
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeAreaFolder("Identity","/Account/Manage", "AdminPolicy");
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Administrator"));
});
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeAreaFolder("Identity","/Account/Manage", "AdminPolicy");
});
━╋う一瞬間旳綻放 2024-08-29 10:04:50

..非常粗鲁地我相信你想要这样的东西?

快速且脏角色管理

[Authorize(Roles = "Admins")]
public ActionResult Register()
{
  ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
  ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
  return View();
}

.. very crudely I believe you want something like this?

Quick and dirty role management

[Authorize(Roles = "Admins")]
public ActionResult Register()
{
  ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
  ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
  return View();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文