Asp.net mvc &引用;“安全意识”;行动链接?

Asp.net mvc &引用;“安全意识”;行动链接?,asp.net-mvc,model-view-controller,security,Asp.net Mvc,Model View Controller,Security,如何创建“安全感知”操作链接,检测用户是否有权单击(调用)该操作? 如果不允许用户使用该操作,则隐藏链接 依靠 web.config(授权)和 [授权]操作的属性 PS 我想在MVC中混用这两种代码是不好的做法?这是从MvcSitemap项目中挖出的一些代码,并为我自己的使用进行了修改。如果我没记错的话,这段代码已经针对MVC2进行了修改,一些函数可能需要重新移植到MVC1 将MVC和FormsAuthentication混合在一起是一种不错的做法,MVC的默认身份验证方法是围绕现有的Asp

如何创建“安全感知”操作链接,检测用户是否有权单击(调用)该操作?
如果不允许用户使用该操作,则隐藏链接

依靠

  • web.config(授权)和
  • [授权]操作的属性
PS

我想在MVC中混用这两种代码是不好的做法?

这是从MvcSitemap项目中挖出的一些代码,并为我自己的使用进行了修改。如果我没记错的话,这段代码已经针对MVC2进行了修改,一些函数可能需要重新移植到MVC1

将MVC和FormsAuthentication混合在一起是一种不错的做法,MVC的默认身份验证方法是围绕现有的Asp.net安全基础架构构建的

确定用户是否具有权限的代码:

public static class SecurityTrimmingExtensions 
{

    public static bool HasActionPermission( this HtmlHelper htmlHelper, string actionName, string controllerName )
    {
        //if the controller name is empty the ASP.NET convention is:
        //"we are linking to a different controller
        ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName) 
                                                ? htmlHelper.ViewContext.Controller
                                                : GetControllerByName(htmlHelper, controllerName);

        var controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo);

        var controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType());

        var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);

        return ActionIsAuthorized(controllerContext, actionDescriptor);
    }


    private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        if (actionDescriptor == null)
            return false; // action does not exist so say yes - should we authorise this?!

        AuthorizationContext authContext = new AuthorizationContext(controllerContext);

        // run each auth filter until on fails
        // performance could be improved by some caching
        foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters)
        {
            authFilter.OnAuthorization(authContext);

            if (authContext.Result != null)
                return false;
        }

        return true;
    }

    private static ControllerBase GetControllerByName(HtmlHelper helper, string controllerName)
    {
        // Instantiate the controller and call Execute
        IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();

        IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName);

        if (controller == null)
        {
            throw new InvalidOperationException(

                String.Format(
                    CultureInfo.CurrentUICulture,
                    "Controller factory {0} controller {1} returned null",
                    factory.GetType(),
                    controllerName));

        }

        return (ControllerBase)controller;
    }

}
Html帮助程序

public static class SecurityTrimmedLink
{
    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName)
    {
        return htmlHelper.HasActionPermission(actionName, "")
                   ? htmlHelper.ActionLink(linkName, actionName)
                   : MvcHtmlString.Create("");
    }        

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, RouteValueDictionary routeValueDictionary )
    {
        return htmlHelper.HasActionPermission(actionName, "")
                   ? htmlHelper.ActionLink(linkName, actionName, routeValueDictionary)
                   : MvcHtmlString.Create("");
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, object routeValues, object htmlAttributes )
    {
        return htmlHelper.HasActionPermission(actionName, "")
                   ? htmlHelper.ActionLink(linkName, actionName, routeValues, htmlAttributes)
                   : MvcHtmlString.Create("");
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, string controllerName)
    {
        return htmlHelper.HasActionPermission(actionName, controllerName)
                   ? htmlHelper.ActionLink(linkName, actionName, controllerName)
                   : MvcHtmlString.Create("");
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, string controllerName, object routeValues, object htmlAttributes)
    {
        return htmlHelper.HasActionPermission(actionName, controllerName)
                   ? htmlHelper.ActionLink(linkName, actionName, controllerName, routeValues, htmlAttributes)
                   : MvcHtmlString.Create("");
    }
}
警告:这在MVC 5中不起作用,因为对FindAction()的调用从未返回操作描述符


我试图找到这个问题,但没能找到,最后我编了一个解决方案(

面积部分比仅仅添加一些重载要复杂一些。 UseNamespaceFallback hack不起作用,因为当您在不同区域中拥有相同名称的控制器时,您将实例化错误的控制器

您需要有一种方法来获取该区域的正确名称空间

否则这个

 IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName); 
会出问题的

目前我在视图中有这样的链接

@Html.SecurityTrimmedActionLink("this link", "Index", "Home",new {Area=string.Empty});   
@Html.SecurityTrimmedActionLink("this link", "Index", "FunctionAdministration", new   {Area="Administration" }, null);
里面

public static bool HasActionPermission(this HtmlHelper htmlHelper, string actionName, string controllerName, object area)
当区域为空时,我将获取该区域的名称空间或默认名称空间

private static string GetNamespaceForArea(string area, RouteCollection routeColl)
    {
        string ns = string.Empty;
        foreach (RouteBase routeBase in routeColl)
        {
            if (routeBase.GetType() == (typeof (Route)))
            {
                Route route = (Route) routeBase;
                RouteValueDictionary dataTokens = route.DataTokens;
                ;
                if (area != null)
                {
                    if (dataTokens.ContainsKey("area"))
                    {
                        if (area.Equals(dataTokens["area"]))
                        {
                            ns = (string) ((string[]) dataTokens["Namespaces"])[0];
                            break;
                        }
                    }
                    else
                    {
                        if (area.Equals(string.Empty))
                        {
                            ns = (string) ((string[]) dataTokens["Namespaces"])[0];
                            break;
                        }
                    }
                }
            }
        }
        return ns;
    }
例如,您需要在GlobalAX中的路由中设置默认名称空间(默认ns“ActionLinkTest.Controllers”):

使用它根据类型名称创建控制器:

ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName) ? htmlHelper.ViewContext.Controller :(ControllerBase) Activator.CreateInstance(Type.GetType(type));
在global.asax中定义区域

areaRegistration.Add("Administration","Areas.Administration");

jfar的代码在很大程度上适用于我,但我不得不对MVC4进行一些修改。这是唯一需要更改的方法:

private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
    if (actionDescriptor == null)
        return false; // action does not exist so say yes - should we authorise this?!

    AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);

    // run each auth filter until on fails
    // performance could be improved by some caching
    foreach (var filter in FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor))
    {
        var authFilter = filter.Instance as IAuthorizationFilter;

        if (authFilter == null)
            continue;

        authFilter.OnAuthorization(authContext);

        if (authContext.Result != null)
            return false;
    }

    return true;
}

在我的例子中,我希望我的逻辑留在控制器端。因此我删除了对
HtmlHelper
的依赖。这些更改很小,但可能会有所帮助

    protected bool HasActionPermission(string actionName, string controllerName)
    {
        if (string.IsNullOrWhiteSpace(controllerName))
            return false;

        var controller = GetControllerByName(ControllerContext.RequestContext, controllerName);
        var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
        var actionDescriptor = controllerDescriptor.FindAction(ControllerContext, actionName);
        return ActionIsAuthorized(ControllerContext, actionDescriptor);
    }

    private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        if (actionDescriptor == null)
            return false; // action does not exist so say yes - should we authorise this?!

        AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);

        // run each auth filter until on fails
        // performance could be improved by some caching
        foreach (var filter in FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor))
        {
            var authFilter = filter.Instance as IAuthorizationFilter;

            if (authFilter == null)
                continue;

            authFilter.OnAuthorization(authContext);

            if (authContext.Result != null)
                return false;
        }

        return true;
    }

    private static ControllerBase GetControllerByName(RequestContext context, string controllerName)
    {
        IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();

        IController controller = factory.CreateController(context, controllerName);

        if (controller == null)
        {
            throw new InvalidOperationException(

                String.Format(
                    CultureInfo.CurrentUICulture,
                    "Controller factory {0} controller {1} returned null",
                    factory.GetType(),
                    controllerName));
        }
        return (ControllerBase)controller;
    }

当您实现代码注释中提到的缓存时,是否可以更新此答案或发布新答案?:-)感谢堆。。。或者指向一个博客或资源,在那里可以随时更新。嘿,我正在努力让它工作,但在使用区域时似乎有问题。您以前在区域中使用过这个吗?为了修复此问题,我将UseNamespaceFallback的原始值存储在一个临时变量中,然后在CreateController之前将令牌设置为true。紧接着,我将其设置回原始值。当您使用Areas和MVC3时,方法private static ControllerBase GetControllerByName(HtmlHelper,string controllerName)似乎无法正常工作,特别是如果您有两个控制器使用相同的名称。当它们有相同名称的控制器时,使其与区域一起工作将非常好。我将@jfar的代码与MvcSiteMapProvider相结合,以提供对区域的支持。如果有人感兴趣,我已经把我在CodePlex上的东西放在了。源代码和二进制.NETFramework4.0DLL都在上面。
    protected bool HasActionPermission(string actionName, string controllerName)
    {
        if (string.IsNullOrWhiteSpace(controllerName))
            return false;

        var controller = GetControllerByName(ControllerContext.RequestContext, controllerName);
        var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
        var actionDescriptor = controllerDescriptor.FindAction(ControllerContext, actionName);
        return ActionIsAuthorized(ControllerContext, actionDescriptor);
    }

    private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        if (actionDescriptor == null)
            return false; // action does not exist so say yes - should we authorise this?!

        AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);

        // run each auth filter until on fails
        // performance could be improved by some caching
        foreach (var filter in FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor))
        {
            var authFilter = filter.Instance as IAuthorizationFilter;

            if (authFilter == null)
                continue;

            authFilter.OnAuthorization(authContext);

            if (authContext.Result != null)
                return false;
        }

        return true;
    }

    private static ControllerBase GetControllerByName(RequestContext context, string controllerName)
    {
        IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();

        IController controller = factory.CreateController(context, controllerName);

        if (controller == null)
        {
            throw new InvalidOperationException(

                String.Format(
                    CultureInfo.CurrentUICulture,
                    "Controller factory {0} controller {1} returned null",
                    factory.GetType(),
                    controllerName));
        }
        return (ControllerBase)controller;
    }