我可以向剃刀视图添加自定义元数据类型吗?

时间:2015-09-14 18:23:39

标签: razor asp.net-mvc-5

目前,我使用从WebViewPage继承的抽象类,为我的MVC项目中的剃刀视图提供函数。我们正在使用自定义登录类/解决方案。我的班级看起来像这样:

public abstract class AuthenticatedViewPageBase : WebViewPage
{
    private Login _user;

    protected override void InitializePage()
    {
        _user = Session["User"] as Login;
    }

    public bool HasPermission(Permissions permission)
    {
        return HasPermission(new List<Permissions> { permission });
    }
    public bool HasPermission(List<Permissions> permissions)
    {
        if (_user == null)
            _user = Session["User"] as Login;

        return _user != null && permissions.Any(thisPerm => _user.Permissions.Any(p => p.PermissionId == (int)thisPerm));
    }

    public bool HasPermission(List<Permissions> permissions, List<PermissionGroups> groups)
    {
        if (_user == null)
            _user = Session["User"] as Login;

        return _user != null &&
            (
                permissions.Any(thisPerm => _user.Permissions.Any(p => p.PermissionId == (int)thisPerm))
                ||
                groups.Any(thisPerm => _user.Permissions.Any(p => p.PermissionGroupId == (int)thisPerm))
            );
    }
}

我的观点如下:

@using PublicationSystem.Model.Enums
@model IEnumerable<PublicationSystem.Model.Profile>

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_LayoutSmBanner.cshtml";
}

@if (HasPermission(new List<Permissions>
{
    Permissions.userCreate
}))
{
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
}

这没关系,但我很乐意能够清理它。我希望在我的AuthenticatedViewPageBase中有一个属性并创建一个增强的ActionLink,这样我就可以这样:

public abstract class AuthenticatedViewPageBase : WebViewPage
{
    //...
    public List<Permissions> ViewPermissions { get; set; }
    //...
}

Index.cshtml:

@using PublicationSystem.Model.Enums
@model IEnumerable<PublicationSystem.Model.Profile>

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_LayoutSmBanner.cshtml";
}

<p>
    @Html.SecureActionLink("Create New", "Create") // checks the @permissions metadata
</p>

Create.cshtml:

@model PublicationSystem.Model.Profile
@ViewPermissions new List<Permissions> {Permissions.userCreate} // This would be the custom 
     // property or metadata field I define in the abstract class

@{
    ViewBag.Title = "Create Profile";
    Layout = "~/Views/Shared/_LayoutSmBanner.cshtml";
}

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    ...
}

如果我可以这样做,我的链接只需要询问目标View需要哪些权限,而不必在每个链接周围包含if语句。

我可以在我的抽象WebViewPage类中添加属性/属性来装饰像我的示例一样的视图吗?如果是这样的话?

自定义ActionLink是否可以“查看”目标View的元数据?

编辑:
我可能正在寻找这样的东西:

[Authorize(Permission="userCreate")]
public ActionResult Create()
{
    //...
}

但我希望成为权限级别,而不是角色级别,我希望根据具有必要权限的用户隐藏/显示我的链接。

1 个答案:

答案 0 :(得分:0)

您可以使用自定义HTML帮助程序轻松完成此操作。以下是接受角色参数的示例SecureLink。如果用户已登录并且指定了任何角色,则链接将可见,否则将无法访问。

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

public static class HtmlHelperExtensions
{
    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, null, 
            new RouteValueDictionary(), new RouteValueDictionary());
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, object routeValues)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, null, 
            new RouteValueDictionary(routeValues), new RouteValueDictionary());
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, string controllerName)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, controllerName, 
            new RouteValueDictionary(), new RouteValueDictionary());
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, RouteValueDictionary routeValues)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, null, 
            routeValues, new RouteValueDictionary());
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, object routeValues, object htmlAttributes)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, null, 
            new RouteValueDictionary(routeValues), 
            HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, RouteValueDictionary routeValues, 
        IDictionary<string, object> htmlAttributes)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, null, 
            routeValues, htmlAttributes);
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, string controllerName, 
        object routeValues, object htmlAttributes)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, controllerName, 
            new RouteValueDictionary(routeValues), 
            HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, string controllerName, 
        RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
    {
        if (string.IsNullOrEmpty(linkText))
        {
            throw new ArgumentNullException("linkText");
        }
        if (!htmlHelper.IsInAnyRole(roles))
        {
            return MvcHtmlString.Create("");
        }
        return MvcHtmlString.Create(HtmlHelper.GenerateLink(
            htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, 
            null, actionName, controllerName, routeValues, htmlAttributes));
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, string controllerName, string protocol, 
        string hostName, string fragment, object routeValues, object htmlAttributes)
    {
        return htmlHelper.SecureLink(linkText, roles, actionName, controllerName, 
            protocol, hostName, fragment, new RouteValueDictionary(routeValues), 
            HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString SecureLink(this HtmlHelper htmlHelper, string linkText, 
        string roles, string actionName, string controllerName, string protocol, 
        string hostName, string fragment, RouteValueDictionary routeValues, 
        IDictionary<string, object> htmlAttributes)
    {
        if (string.IsNullOrEmpty(linkText))
        {
            throw new ArgumentNullException("linkText");
        }
        if (!htmlHelper.IsInAnyRole(roles))
        {
            return MvcHtmlString.Create("");
        }
        return MvcHtmlString.Create(HtmlHelper.GenerateLink(
            htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, 
            null, actionName, controllerName, protocol, hostName, fragment, routeValues, 
            htmlAttributes));
    }

    private static bool IsInAnyRole(this HtmlHelper htmlHelper, string roles)
    {
        var user = htmlHelper.ViewContext.HttpContext.User;
        if (user == null || user.Identity == null || !user.Identity.IsAuthenticated)
        {
            return false;
        }
        if (string.IsNullOrEmpty(roles))
        {
            return true;
        }
        return user.IsInAnyRole(SplitString(roles));
    }

    private static bool IsInAnyRole(this IPrincipal user, IEnumerable<string> roles)
    {
        foreach (var role in roles)
        {
            if (user.IsInRole(role))
            {
                return true;
            }
        }
        return false;
    }

    private static string[] SplitString(string original)
    {
        if (String.IsNullOrEmpty(original))
        {
            return new string[0];
        }

        var split = from piece in original.Split(',')
                    let trimmed = piece.Trim()
                    where !String.IsNullOrEmpty(trimmed)
                    select trimmed;
        return split.ToArray();
    }
}

用法

@Html.SecureLink("Logged In Users Will See This", "", "About", "Home")
@Html.SecureLink("Admins and Managers Will See This", "Admin,Manager", "About", "Home")
@Html.ActionLink("Everyone Will See This", "About", "Home")

另一种选择是使用MvcSiteMapProvider's security trimming feature,它会自动使用AuthorizeAttribute(或其任何子类)来控制导航链接的可见性。

  

完全披露:我是MvcSiteMapProvider项目的主要撰稿人。

但是你完成了工作,你应该使用内置的IPrincipalIIdendity接口以及MVC的AuthorizeAttribute而不是重新发明轮子。您可以轻松扩展AuthorizeAttribute以使用任何权限方案。

将这种额外的混乱添加到视图基类不是一个好方法。