目前,我使用从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()
{
//...
}
但我希望成为权限级别,而不是角色级别,我希望根据具有必要权限的用户隐藏/显示我的链接。
答案 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
项目的主要撰稿人。
但是你完成了工作,你应该使用内置的IPrincipal
和IIdendity
接口以及MVC的AuthorizeAttribute
而不是重新发明轮子。您可以轻松扩展AuthorizeAttribute
以使用任何权限方案。
将这种额外的混乱添加到视图基类不是一个好方法。