Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc 如何创建特定于区域、控制器和操作的自定义属性?_Asp.net Mvc_Attributes_Asp.net Mvc 3 - Fatal编程技术网

Asp.net mvc 如何创建特定于区域、控制器和操作的自定义属性?

Asp.net mvc 如何创建特定于区域、控制器和操作的自定义属性?,asp.net-mvc,attributes,asp.net-mvc-3,Asp.net Mvc,Attributes,Asp.net Mvc 3,换句话说,这真的是一个愚蠢的想法吗 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeActionAttribute : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { // get the ar

换句话说,这真的是一个愚蠢的想法吗

[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);
    }
}
更新 我试图避免以下情况,在这种情况下,我们拥有非常精细的角色权限,因为这些角色是基于每个客户端设置的,并连接到用户组:

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以访问路由信息并将其用作角色名称吗?正如李维所说,RouteData.价值观是不安全的

使用正在执行的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}", path, verb);
    string insecureRoleName = 
        String.Format("{0}/{1}/{2}/{3}", 
        insecureArea, 
        insecureController, 
        insecureAction, 
        verb);
    string secureRoleName = 
        String.Format("{0}/{1}/{2}/{3}", 
        area, 
        controller, 
        action, 
        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属性访问控制器的类型和操作的MethodInfo。这是确定MVC管道处理时真正执行的操作的唯一可靠方法,因为您的查找可能与MVC的幕后操作不完全匹配。一旦您有了类型/MethodInfo/where,您就可以使用您想要的任何信息(例如它们的完全限定名)来做出安全决策

< P >作为一个实际例子,考虑一个区域MyCaleas,控制器脚控制器和动作动作。通常,点击此FooController::动作的方式是通过以下URL:

/MyArea/Foo/TheAction

路由给出元组(Area=“MyArea”、Controller=“Foo”、Action=“TheAction”)

但是,您也可以通过以下URL点击FooController::TheAction:

/Foo/TheAction

路由将给出元组(Area=”“,Controller=“Foo”,Action=“TheAction”)。请记住,区域与管线关联,而不是与控制器关联。由于一个控制器可以被多个路由击中(如果定义匹配),那么控制器也可以与多个区域逻辑关联。这就是为什么我们告诉开发人员永远不要使用路由(或区域或标签,通过扩展)来做出安全决策的原因

此外,类中还有一个bug,因为它是可变的(它在OnAuthorization中改变了自己的Roles属性)。操作筛选器属性必须是不可变的,因为它们可能被管道的某些部分缓存并重用。根据此属性在应用程序中声明的位置,这会打开一个计时攻击,恶意网站访问者可以利用该攻击来授予自己访问任何他希望的操作的权限

有关更多信息,请参见我的回复:


如果您想这样做,考虑到Levi的建议,答案如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace MvcApplication1.Extension.Attribute
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthorizeActionAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// Called when a process requests authorization.
        /// </summary>
        /// <param name="filterContext">The filter context, which encapsulates information for using <see cref="T:System.Web.Mvc.AuthorizeAttribute"/>.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="filterContext"/> parameter is null.</exception>
        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;
            }

            // these values combined are our roleName
            string roleName = GetRoleName(filterContext);

            if (!filterContext.HttpContext.User.IsInRole(roleName))
            {
                filterContext.Controller.TempData.Add("RedirectReason", "You are not authorized to access this page.");
                filterContext.Result = new RedirectResult("~/Error/Unauthorized");

                return;
            }

            //
            base.OnAuthorization(filterContext);
        }

        /// <summary>
        /// Gets the name of the role. Theorectical construct that illustrates a problem with the
        /// area name. RouteData is apparently insecure, but the area name is available there.
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        /// <param name="version">The version.</param>
        /// <returns></returns>
        string GetRoleName(AuthorizationContext filterContext)
        {
            //
            var verb = filterContext.HttpContext.Request.HttpMethod;

            // recommended way to access controller and action names
            var controllerFullName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerType.FullName;
            var actionName = filterContext.ActionDescriptor.ActionName;

            return String.Format("{0}.{1}-{2}", controllerFullName, actionName, verb);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Web;
使用System.Web.Mvc;
使用System.Web.Security;
命名空间MVCAPApplication1.Extension.Attribute
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
公共类AuthorizeActionAttribute:AuthorizeAttribute
{
/// 
///当进程请求授权时调用。
/// 
///筛选器上下文,它封装了要使用的信息。
///参数为空。
授权时的公共覆盖无效(AuthorizationContext filterContext)
{
如果(filterContext==null)
{
抛出新ArgumentNullException(“filterContext”);
}
如果(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
//身份验证失败,重定向到登录页面
filterContext.Result=新的HttpUnauthorizedResult();
返回;
}
//这些值组合在一起就是我们的角色名称
字符串roleName=GetRoleName(filterContext);
if(!filterContext.HttpContext.User.IsInRole(roleName))
{
filterContext.Controller.TempData.Add(“重定向原因”,“您无权访问此页面”);
filterContext.Result=新重定向结果(“~/Error/Unauthorized”);
返回;
}
//
基于授权(filterContext);
}
/// 
///获取角色的名称。理论构造说明了
///区域名称。RouteData显然不安全,但区域名称在那里可用。
/// 
///过滤器上下文。
///版本。
/// 
字符串GetRoleName(授权上下文筛选器上下文)
{
//
var verb=filterContext.HttpContext.Request.HttpMethod;
//访问控制器和操作名称的推荐方法
var controllerFullName=filterContext.ActionDescriptor.ControllerDescriptor.ControllerType.FullName;
var actionName=filterContext.ActionDescriptor.actionName;
返回String.Format(“{0}.{1}-{2}”,controllerFullName,actionName,verb);
}
}
}

我不想在用户未担任角色的情况下提供HttpUnauthorizedResult,因为结果是将用户发送到登录页面。考虑到他们已经登录,这对用户来说非常混乱。

这是一个简短的通知!确保使用
filterContext.RouteData.DataTokens[“area”]
而不是
filterContext.RouteData.Values[“area”]

好卢