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

如何在ASP.NET MVC中为整个区域设置授权?,asp.net,asp.net-mvc-2,authorization,asp.net-mvc-areas,Asp.net,Asp.net Mvc 2,Authorization,Asp.net Mvc Areas,我有一个管理员区域,我只希望管理员进入该区域。我考虑将Authorized属性添加到管理区域中的每个控制器。难道没有一个优雅的解决方案,或者框架本身没有这个特性吗 编辑: 对不起,我应该在这之前提一下。我正在使用从AuthorizeAttribute派生的自定义AuthorizedAttribute。。。非常粗糙我相信你想要这样的东西 如果所有管理代码都在一个控制器中,则将Authorize添加到整个类中 [Authorize] public class AdminController : C

我有一个管理员区域,我只希望管理员进入该区域。我考虑将Authorized属性添加到管理区域中的每个控制器。难道没有一个优雅的解决方案,或者框架本身没有这个特性吗

编辑:
对不起,我应该在这之前提一下。我正在使用从AuthorizeAttribute派生的自定义AuthorizedAttribute。

。。非常粗糙我相信你想要这样的东西


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

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

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

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


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

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

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

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

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

我刚刚开始讨论这个。。。但到目前为止,这对我来说非常有效

我创建了一个自定义AuthorizeAttribute类,并将其添加到RegisterGlobalFilters函数中

在CustomAttribute中,我根据它所在的区域检查各种条件

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

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

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

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

在您的业务逻辑中,这允许您以多种方式使用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)
    {
        ...
    }
public class LocationsController : Controller
{

    // GET: Locations
    public ActionResult Index()
    {
        //Business logic here.
        return View(...);
    }
}
一些注释。

如果要将特定操作的访问权限限制为特定角色,则必须使用
[OverrideAuthorization]
属性。否则,
[Authorize]
属性属性将被忽略,并且仅允许默认角色(在我的示例中为管理员),即使您由于全局筛选器而指定了其他角色(例如Dispatcher等)。任何未经授权的用户都将被重定向到登录屏幕

使用
[OverrideAuthorization]
属性会导致操作忽略您设置的全局筛选器。因此,每当使用覆盖时,必须重新应用
[Authorize]
属性,以便操作保持安全

关于整个区域和控制器


要按区域进行限制,如您所问,请在控制器上放置
[OverrideAuthorization]
[Authorization]
属性,而不是单个操作。

使用区域名称,在startup中只使用带
AuthorizationAreaFolder
的斜杠。cs对我来说很有效:

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

好的,这是一个好方法来确保一个控制器只通过一个URL被击中吗?可能是通过正确的路线规划?无法确保控制器只能通过单个URL访问。路由只是访问控制器的一种机制;它们不是机制。这就是为什么任何安全属性都需要直接应用于控制器本身,
public class LocationsController : Controller
{

    // GET: Locations
    public ActionResult Index()
    {
        //Business logic here.
        return View(...);
    }
}
services.AddRazorPages()
        .AddRazorPagesOptions(options => options.Conventions.AuthorizeAreaFolder("Admin", "/"))
        .WithRazorPagesAtContentRoot();