[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeActionAttribute : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { // get the area,controller and action var area = filterContext.RouteData.Values["area"]; var controller = filterContext.RouteData.Values["controller"]; var action = filterContext.RouteData.Values["action"]; string verb = filterContext.HttpContext.Request.HttpMethod; // these values combined are our roleName string roleName = String.Format("{0}/{1}/{2}/{3}",area,controller,action,verb); // set role name to area/controller/action name this.Roles = roleName; base.OnAuthorization(filterContext); } }
UPDATE
在我们拥有极其精细的角色权限的情况下,我试图避免以下情况,因为角色是基于每个客户端设置的并附加到用户组:
public partial class HomeController : Controller { [Authorize(Roles = "/supplierarea/homecontroller/indexaction/")] public virtual ActionResult Index() { return View(); } [Authorize(Roles = "/supplierarea/homecontroller/aboutaction/")] public virtual ActionResult About() { return View(); } }
任何人都可以通过安全的方式来启发我编写此AuthorizeRouteAttribute以访问路由信息并将其用作角色名称吗?正如Levi所说,RouteData.Values并不安全.
是否使用执行httpContext.Request.Path更安全或更好的做法?
public override void OnAuthorization(AuthorizationContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { // auth Failed,redirect to login page filterContext.Result = new HttpUnauthorizedResult(); return; } var path = filterContext.HttpContext.Request.Path; var verb = filterContext.HttpContext.Request.HttpMethod; // these values combined are our roleName string roleName = String.Format("{0}/{1}",path,verb); if (!filterContext.HttpContext.User.IsInRole(roleName)) { // role auth Failed,redirect to login page filterContext.Result = new HttpUnauthorizedResult(); // P.S. I want to tell the logged in user they don't // have access,not ask them to login. They are already // logged in! return; } // base.OnAuthorization(filterContext); }
这可能会进一步说明问题:
enum Version { PathBasedRole,InsecureButWorks,SecureButMissingAreaName } string GetRoleName(AuthorizationContext filterContext,Version version) { // var path = filterContext.HttpContext.Request.Path; var verb = filterContext.HttpContext.Request.HttpMethod; // recommended way to access controller and action names var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; var action = filterContext.ActionDescriptor.ActionName; var area = "oh dear...."; // mmmm,where's thearea name??? // var insecureArea = filterContext.RouteData.Values["area"]; var insecureController = filterContext.RouteData.Values["controller"]; var insecureAction = filterContext.RouteData.Values["action"]; string pathRoleName = String.Format("{0}/{1}",verb); string insecureRoleName = String.Format("{0}/{1}/{2}/{3}",insecureArea,insecureController,insecureAction,verb); string secureRoleName = String.Format("{0}/{1}/{2}/{3}",verb); string roleName = String.Empty; switch (version) { case Version.InsecureButWorks: roleName = insecureRoleName; break; case Version.PathBasedRole: roleName = pathRoleName; break; case Version.SecureButMissingAreaName: // let's hope they don't choose this,because // I have no idea what the area name is roleName = secureRoleName; break; default: roleName = String.Empty; break; } return roleName; }
解决方法
如果确实需要,可以使用控制器的类型或操作的MethodInfo来做出安全决策.但基于字符串的所有东西都是在寻找麻烦.请记住,没有保证路由值到实际控制器的1:1映射.如果您正在使用路由元组(a,b,c)来验证对SomeController :: SomeAction的访问,但有人发现(a,b’,c)也会触发相同的操作,那么该人可以绕过您的安全机制.
您可以通过filterContext参数的ActionDescriptor属性访问控制器的Type和action的MethodInfo.这是确定在MVC管道处理时将真正执行什么操作的唯一确定方法,因为您的查找可能与MVC幕后发生的事件不完全匹配.获得Type / MethodInfo /之后,您可以使用您希望的任何信息(例如其完全限定的名称)来做出安全决策.
作为一个实际示例,考虑一个带有控制器FooController的区域MyArea和一个动作TheAction.通常你通过这个URL点击这个FooController :: TheAction的方式:
/MyArea/Foo/TheAction
并且路由给出了元组(Area =“MyArea”,Controller =“Foo”,Action =“TheAction”).
但是,您也可以通过以下URL点击FooController :: TheAction:
/Foo/TheAction
并且路由将给出元组(Area =“”,Action =“TheAction”).请记住,区域与路径相关联,而不是控制器.并且由于控制器可以被多个路径击中(如果定义匹配),则控制器也可以在逻辑上与多个区域相关联.这就是为什么我们告诉开发人员永远不要使用路由(或区域或< location>标记,通过扩展)来做出安全决策.
另外,你的类中有一个错误,它是可变的(它在OnAuthorization中改变它自己的Roles属性).操作过滤器属性必须是不可变的,因为它们可以被管道的某些部分缓存并重用.根据应用程序中声明此属性的位置,这会打开一个计时攻击,然后恶意网站访问者可以利用该攻击授予自己访问他希望的任何操作的权限.
有关详细信息,请参阅我的回复:
> Area level security for asp.net mvc
> How to get currently executing area?