卡住了创建"修剪安全性"的html.ActionLink扩展方法

时间:2020-03-06 14:35:57  来源:igfitidea点击:

我正在尝试为MVC的htmlHelper创建扩展方法。
目的是基于控制器/操作上设置的AuthorizeAttribute启用或者禁用ActionLink。
从MVCSitemap借用
Maarten Balliauw创建的代码,我想先确定用户对控制器/操作的权限,然后再决定如何呈现操作链接。
当我尝试获取MvcHandler时,我得到一个空值。
有没有更好的方法来设置控制器/动作的属性?

这是扩展方法的代码:

public static class HtmlHelperExtensions
{
    public static string SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller)
    {
        //simplified for brevity 
        if (IsAccessibleToUser(action, controller))
        {
            return htmlHelper.ActionLink(linkText, action,controller);    
        }
        else
        {
            return String.Format("<span>{0}</span>",linkText);    
        }
    }

    public static bool IsAccessibleToUser(string action, string controller)
    {
        HttpContext context = HttpContext.Current;

        MvcHandler handler = context.Handler as MvcHandler;            

        IController verifyController = 
            ControllerBuilder
            .Current
            .GetControllerFactory()
            .CreateController(handler.RequestContext, controller);

        object[] controllerAttributes = verifyController.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true);
        object[] actionAttributes = verifyController.GetType().GetMethod(action).GetCustomAttributes(typeof(AuthorizeAttribute), true);

        if (controllerAttributes.Length == 0 && actionAttributes.Length == 0)
            return true;

        IPrincipal principal = handler.RequestContext.HttpContext.User;

        string roles = "";
        string users = "";
        if (controllerAttributes.Length > 0)
        {
            AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute;
            roles += attribute.Roles;
            users += attribute.Users;
        }
        if (actionAttributes.Length > 0)
        {
            AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute;
            roles += attribute.Roles;
            users += attribute.Users;
        }

        if (string.IsNullOrEmpty(roles) && string.IsNullOrEmpty(users) && principal.Identity.IsAuthenticated)
            return true;

        string[] roleArray = roles.Split(',');
        string[] usersArray = users.Split(',');
        foreach (string role in roleArray)
        {
            if (role != "*" && !principal.IsInRole(role)) return false;
        }
        foreach (string user in usersArray)
        {
            if (user != "*" && (principal.Identity.Name == "" || principal.Identity.Name != user)) return false;
        }
        return true;
    }

}

解决方案

ViewPage引用了视图上下文,因此我们可以将其作为扩展方法。

然后,我们可以说是Request.IsAuthenticated还是Request.User.IsInRole(...)

用法将类似于" <%= this.SecurityLink(文本,demandRole,控制器,操作,值)%>"

这是工作代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal;
using System.Web.Routing;
using System.Web.Mvc;
using System.Collections;
using System.Reflection;
namespace System.Web.Mvc.Html
{
    public static class HtmlHelperExtensions
    {
        public static string SecurityTrimmedActionLink(
        this HtmlHelper htmlHelper,
        string linkText,
        string action,
        string controller)
        {
            return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, false);
        }
        public static string SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller, bool showDisabled)
        {
            if (IsAccessibleToUser(action, controller))
            {
                return htmlHelper.ActionLink(linkText, action, controller);
            }
            else
            {
                return showDisabled ? String.Format("<span>{0}</span>", linkText) : "";
            }
        }
        public static bool IsAccessibleToUser(string actionAuthorize, string controllerAuthorize)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            GetControllerType(controllerAuthorize);
            Type controllerType = GetControllerType(controllerAuthorize);
            var controller = (IController)Activator.CreateInstance(controllerType);
            ArrayList controllerAttributes = new ArrayList(controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true));
            ArrayList actionAttributes = new ArrayList();
            MethodInfo[] methods = controller.GetType().GetMethods();
            foreach (MethodInfo method in methods)
            {
                object[] attributes = method.GetCustomAttributes(typeof(ActionNameAttribute), true);
                if ((attributes.Length == 0 && method.Name == actionAuthorize) || (attributes.Length > 0 && ((ActionNameAttribute)attributes[0]).Name == actionAuthorize))
                {
                    actionAttributes.AddRange(method.GetCustomAttributes(typeof(AuthorizeAttribute), true));
                }
            }
            if (controllerAttributes.Count == 0 && actionAttributes.Count == 0)
                return true;

            IPrincipal principal = HttpContext.Current.User;
            string roles = "";
            string users = "";
            if (controllerAttributes.Count > 0)
            {
                AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute;
                roles += attribute.Roles;
                users += attribute.Users;
            }
            if (actionAttributes.Count > 0)
            {
                AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute;
                roles += attribute.Roles;
                users += attribute.Users;
            }

            if (string.IsNullOrEmpty(roles) && string.IsNullOrEmpty(users) && principal.Identity.IsAuthenticated)
                return true;

            string[] roleArray = roles.Split(',');
            string[] usersArray = users.Split(',');
            foreach (string role in roleArray)
            {
                if (role == "*" || principal.IsInRole(role))
                    return true;
            }
            foreach (string user in usersArray)
            {
                if (user == "*" && (principal.Identity.Name == user))
                    return true;
            }
            return false;
        }

        public static Type GetControllerType(string controllerName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            foreach (Type type in assembly.GetTypes())
            {
                if (type.BaseType.Name == "Controller" && (type.Name.ToUpper() == (controllerName.ToUpper() + "Controller".ToUpper())))
                {
                    return type;
                }
            }
            return null;
        }
    }
}

我不喜欢使用反射,但是无法进入ControllerTypeCache。