Подтвердить что ты не робот

Ссылка на ссылку "Безопасность"?

Как я могу создать ссылку на действие "безопасность", которая определяет, разрешено ли пользователю кликать (вызывать) действие?
Скрыть ссылку, если пользователю не разрешено использовать это действие...

В зависимости от

  • web.config(авторизация) и
  • [Авторизовать] атрибуты при действиях

PS
Я думаю, что это плохая практика, чтобы смешать эти 2 в MVC?

4b9b3361

Ответ 1

Это код, извлеченный из проекта 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() никогда не возвращает дескриптор действия

Я попытался найти проблему и не смог и закончил программирование работы.: (

Ответ 2

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;
}

Ответ 3

Часть области немного сложнее, чем просто добавление некоторых перегрузок. Взлом UseNamespaceFallback не работает, потому что вы создадите неверный код, когда у вас есть идентично названные контроллеры в разных областях.

У вас должен быть способ получить правильное пространство имен для области

в противном случае это

 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;
    }

Вам нужно настроить пространство имен по умолчанию в своем маршруте в globalasax, например, так (по умолчанию ns "ActionLinkTest.Controllers" ):

routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            , new[] { "ActionLinkTest.Controllers" }
        );

используйте его для создания контроллера на основе имени типа:

ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName) ? htmlHelper.ViewContext.Controller :(ControllerBase) Activator.CreateInstance(Type.GetType(type));

в global.asax определите области

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