我有一个管理员区域,我只希望管理员进入该区域。我考虑将 Authorized 属性添加到管理区域中的每个控制器。是不是有一个优雅的解决方案,或者框架本身没有这个功能?
编辑:对不起,我应该在之前提到这一点。我正在使用从 AuthorizeAttribute 派生的自定义 AuthorizedAttribute。
我有一个管理员区域,我只希望管理员进入该区域。我考虑将 Authorized 属性添加到管理区域中的每个控制器。是不是有一个优雅的解决方案,或者框架本身没有这个功能?
编辑:对不起,我应该在之前提到这一点。我正在使用从 AuthorizeAttribute 派生的自定义 AuthorizedAttribute。
基于 Web.config 的安全性几乎不应该在 MVC 应用程序中使用。这样做的原因是多个 URL 可能会命中一个控制器,并且将这些检查放在 Web.config 中总是会遗漏一些东西。请记住 - 控制器与区域无关,路线与区域相关联。如果没有冲突,MVC 控制器工厂将很乐意为来自 Areas/ 文件夹的控制器提供非区域请求。
例如,使用默认项目结构,添加一个带有 AdminDefaultController 的管理区域,您可以通过 /Admin/AdminDefault/Index和/AdminDefault/Index 访问此控制器。
唯一受支持的解决方案是将您的属性放在控制器基类上,并确保区域内的每个控制器都是该基类的子类。
我一直在调查同样的问题。由于不可能根据区域保护控制器,因此想到了一个更简单的选择。
为覆盖 Controller 的每个区域创建一个基本控制器定义,并为此添加安全要求。然后你只需要确保区域中的每个控制器都覆盖 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]
and装饰它[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]
and[Authorize]
属性放在控制器上,而不是单独的操作上。
AuthorizeAreaFolder
在 startup.cs 中使用区域名称和斜杠对我有用:
services.AddRazorPages()
.AddRazorPagesOptions(options => options.Conventions.AuthorizeAreaFolder("Admin", "/"))
.WithRazorPagesAtContentRoot();
..非常粗略我相信你想要这样的东西?
[Authorize(Roles = "Admins")]
public ActionResult Register()
{
ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}