我们如何在 ASP.NET MVC 中为整个区域设置授权?
Posted
技术标签:
【中文标题】我们如何在 ASP.NET MVC 中为整个区域设置授权?【英文标题】:How can we set authorization for a whole area in ASP.NET MVC? 【发布时间】:2011-01-20 02:55:09 【问题描述】:我有一个管理员区域,我只希望管理员进入该区域。我考虑将 Authorized 属性添加到管理区域中的每个控制器。是不是没有一个优雅的解决方案,或者框架本身没有这个功能?
编辑: 对不起,我应该在前面提到这一点。我正在使用从 AuthorizeAttribute 派生的自定义 AuthorizedAttribute。
【问题讨论】:
见我的博文Securing your ASP.NET MVC 3 Application 查看我的博客文章保护您的 ASP.NET MVC 4 应用程序和新的 AllowAnonymous 属性 Rick 最后评论的链接 -> blogs.msdn.com/b/rickandy/archive/2012/03/23/… 【参考方案1】:基于 Web.config 的安全性几乎应该从在 MVC 应用程序中使用。这样做的原因是多个 URL 可能会命中一个控制器,并且将这些检查放在 Web.config 中总是会遗漏一些东西。请记住 - 控制器与区域无关,路线与区域相关联。如果没有冲突,MVC 控制器工厂很乐意为 Areas/ 文件夹中的控制器提供非区域请求。
例如,使用默认项目结构,添加一个带有 AdminDefaultController 的 Admin 区域,您可以通过 /Admin/AdminDefault/Index 和 /AdminDefault/Index 来点击这个控制器。
唯一受支持的解决方案是将您的属性放在控制器基类上,并确保区域内的每个控制器都是该基类的子类。
【讨论】:
嗯,是确保控制器仅通过单个 URL 被击中的好方法吗?通过正确的路线规划可能吗? 无法确保只能通过单个 URL 访问控制器。路由只是访问控制器的一种机制;它们不是机制。这就是为什么任何安全属性都需要直接应用于控制器本身,而不是路由(以及扩展的区域)。例如,考虑在 MVC 3 中引入 MvcHandler.ashx。这将通过绕过 all 的 Routing 直接调用 MVC 框架。 正确。始终假设每个控制器的每个公共方法都可以通过网络调用。那么 [Authorize]、[NonAction] 和其他属性是保护这些资源的正确方法是有道理的。 当然,更明智的方法是提供一种机制,无论您的论点如何,都可以在给定区域的所有控制器上强制过滤。开发人员更有可能忘记添加属性。就像系统人员建议他们的用户单独保护文件夹中的每个文件一样,因为 Windows 会阻止系统人员在文件夹级别执行此操作。另一个半生不熟的想法 IMO。看在上帝的份上,保持干燥! 来自未来的您好 - 属性路由是一种更好的方式来控制您的路由,并且更容易理解 - 方式,随机多条路由到同一页面的机会更少。强烈推荐。【参考方案2】:我刚刚调查了同样的问题。由于不可能根据区域保护控制器,因此想到了一个更简单的选项。
为覆盖控制器的每个区域创建一个基本控制器定义,并为此添加安全要求。然后你只需要确保区域中的每个控制器都覆盖 AreaController 而不是 Controller。例如:
/// <summary>
/// Base controller for all Admin area
/// </summary>
[Authorize(Roles = "Admin")]
public abstract class AdminController : Controller
它仍然需要您从这个基础派生管理区域中的每个控制器,
public class HomeController : AdminController
// .. actions
但至少您有一个点可以定义该区域的安全性。
【讨论】:
是的,这听起来确实是个好主意。除了 MSDN 还为其他问题提出了类似的解决方案。传承是好的。我喜欢这个答案。 我真的不明白为什么你会在管理部分中对每个控制器进行子类化,而不是简单地在类定义上方编写你的 1 行属性。 简单的答案是 DRY - en.wikipedia.org/wiki/Don't_repeat_yourself - 我可以在一行代码中更改受保护的角色,而不是寻找每个 [Authorize] 属性 这对我来说看起来很干净。但是,如果我旁边的任何开发人员忘记在他的新控制器内部区域中继承 AdminController 怎么办?接下来,当我在我的实现(identityserver3)中添加它并且用户直接点击区域控制器时,我看到身份服务器登录页面上的东西一直在循环。 @AbhimanyuKumarVatsa 开发人员忘记从基本控制器继承与开发人员忘记向控制器添加[Authorize]
属性的问题相同。解决方案不是这里的错误。【参考方案3】:
我刚开始做这个……但到目前为止,这对我来说效果很好。
我创建了一个自定义 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;
【讨论】:
【参考方案4】:如果您的所有管理代码都在一个控制器中,则将 Authorize 添加到整个类。
[Authorize]
public class AdminController : Controller
.......
【讨论】:
这适用于单个控制器。但是我们如何为整个区域做到这一点呢? @ppumkin,Inherited
设置为 true
为 AuthorizeAttribute
所以只需继承它。 msdn.microsoft.com/en-us/library/…【参考方案5】:
当前接受的答案并不是最安全的解决方案,因为它要求开发人员始终记住为任何新的控制器或操作(“黑名单”;允许用户访问所有内容)继承该新基类除非手动限制操作)。当不熟悉您的习惯的新开发人员被引入项目时,这尤其会导致问题。如果这样做,很容易忘记继承正确的控制器类,尤其是在您将注意力从项目上移开数周、数月或数年之后。如果开发者忘记继承,则说明项目存在安全漏洞并不明显。
对此问题的更安全的解决方案是拒绝访问所有请求,然后用允许访问操作的角色装饰每个操作(“白名单”;阻止所有用户访问,除非手动允许)。现在,如果开发人员忘记将适当的授权列入白名单,用户会通知您,这就像查看其他控制器以提醒有关如何提供适当的访问权限一样简单。但是,至少没有重大的安全漏洞。
在 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 - 只有经过身份验证的用户才能访问 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]
属性放在控制器上,而不是单独的操作上。
【讨论】:
【参考方案6】:在 startup.cs 中使用区域名称和带有 AuthorizeAreaFolder
的斜线对我有用:
services.AddRazorPages()
.AddRazorPagesOptions(options => options.Conventions.AuthorizeAreaFolder("Admin", "/"))
.WithRazorPagesAtContentRoot();
【讨论】:
【参考方案7】:.. 非常粗略,我相信你想要这样的东西?
Quick and dirty role management
[Authorize(Roles = "Admins")]
public ActionResult Register()
ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
【讨论】:
以上是关于我们如何在 ASP.NET MVC 中为整个区域设置授权?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 HTML 中为 ASP.NET MVC 创建一个按钮? [复制]
如何在 ASP.NET MVC 4 中为 CSP 使用动态随机数