我想根据用户的授权显示/隐藏编辑/删除链接(包括菜单项)。我已经实现了AuthorizeAttribute,并为覆盖AuthorizeCore的角色检查提供了自定义逻辑。我想在检查用户是否有权查看LinkExtensions方法中的编辑/删除链接时使用该逻辑。 这是我的设置:
public class AuthorizeActivity : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
string actionType = httpContext.Request.HttpMethod;
string controller = httpContext.Request.RequestContext.RouteData.Values["controller"].ToString();
string action = httpContext.Request.RequestContext.RouteData.Values["action"].ToString();
//ADMINS
if (controller == "Admin")
{
if (httpContext.User.IsInRole(Constants.Admin))
return true;
}
else
{
//DATA READERS ONLY
if ((action == "Details") || (action == "Index"))
{
if (httpContext.User.IsInRole(Constants.DataReader))
return true;
}
//DATA WRITERS & IT
else
{
...
}
}
return false;
}
我还使用了Vivien Chevallier的逻辑来创建此处概述的授权操作链接扩展:http://vivien-chevallier.com/Articles/create-an-authorized-action-link-extension-for-aspnet-mvc-3 现在在我看来我可以使用:
<li>@Html.ActionLinkAuthorized("Admin", "Index", "Admin",false) </li>
根据用户的权利,链接将显示或不显示。 在我的控制器中,动作装饰有:
[AuthorizeActivity]
public ActionResult Index()
{
return View(view);
}
授权链接不起作用,除非我在属性中指定“角色”,我认为这是多余的,如下所示:
[AuthorizeActivity(Roles = Constants.roleSalesContractAdmin)]
public ActionResult Index()
{
return View(view);
}
我似乎找不到在AuthorizeAttribute中重用逻辑的方法。理想情况下,它会在ActionLinkAuthorized中调用,就像Vivien所拥有的那样:
public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool showActionLinkAsDisabled)
{
if (htmlHelper.ActionAuthorized(actionName, controllerName)) //The call to verify here -- or inside ActionAuthorized
{
return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
}
else
{
if (showActionLinkAsDisabled)
{
TagBuilder tagBuilder = new TagBuilder("span");
tagBuilder.InnerHtml = linkText;
return MvcHtmlString.Create(tagBuilder.ToString());
}
else
{
return MvcHtmlString.Empty;
}
}
}
这是ActionAuthorized方法。 OnAuthorization调用不会转到自定义调用
public static bool ActionAuthorized(this HtmlHelper htmlHelper, string actionName, string controllerName)
{
ControllerBase controllerBase = string.IsNullOrEmpty(controllerName) ? htmlHelper.ViewContext.Controller : htmlHelper.GetControllerByName(controllerName);
ControllerContext controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerBase);
ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(controllerContext.Controller.GetType());
ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
if (actionDescriptor == null)
return false;
FilterInfo filters = new FilterInfo(FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor));
AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter authorizationFilter in filters.AuthorizationFilters)
{
authorizationFilter.OnAuthorization(authorizationContext); //This call
if (authorizationContext.Result != null)
return false;
}
return true;
}
答案 0 :(得分:6)
在您看来,您可以写:
@if (User.IsInRole("role"))
{
<li>@Html.ActionLink("Words", "View", "Controller")</li>
<li>@Html.ActionLink("Words", "View", "Controller")</li>
}
...假设他们已登录,则会有条件地隐藏链接
答案 1 :(得分:1)
使用授权属性装饰操作或控制器时,仅在授权用户时才执行操作。这意味着如果用户未被授权,则视图(将包含所有授权的链接扩展名)根本不会被渲染。
因此,您需要将属性中的授权逻辑与html扩展的逻辑分开。
我还注意到,在属性的授权核心中,您正在执行以下操作:
if ((action == "Details") || (action == "Index"))
{
if (httpContext.User.IsInRole(Constants.DataReader))
return true;
}
非常糟糕的主意!您不应在授权核心逻辑中指定操作名称! 你需要做的就是装饰&#34;细节&#34;和&#34;索引&#34;具有默认授权属性的方法具有适当的角色:
[Authorize(Roles=Constants.DataReader)]
public ActionResult Index()
{
}
现在关于角色相关助手:
你可以这样做:public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string roles, other arguments)
{
//assuming that roles are passed as coma separated strings
var rolesList = roles.Split(",",roles);
bool shouldShow = false;
foreach(var role in rolesList )
{
if (HttpContext.User.IsInRole(role))
{
shouldShow = true;
break;
}
}
if(shouldShow)
{
//return your extension representation
}
else
{
//fallback
}
}
答案 2 :(得分:0)
我有类似的问题 我这样解决了:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class MyAuthorizedAttribute : AuthorizeAttribute
{
public bool CheckPermissions(HttpContextBase httpContext, string controller, string action)
{
bool authorized;
//Validate User permissions of the way you think is best
return authorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var action = filterContext.ActionDescriptor.ActionName;
var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
if (filterContext == null)
{
throw new ArgumentNullException(nameof(filterContext));
}
if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
{
// If a child action cache block is active, we need to fail immediately, even if authorization
// would have succeeded. The reason is that there's no way to hook a callback to rerun
// authorization before the fragment is served from the cache, so we can't guarantee that this
// filter will be re-run on subsequent requests.
throw new InvalidOperationException("AuthorizeAttribute Cannot Use Within Child Action Cache");
}
var skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true)
||
filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
typeof (AllowAnonymousAttribute), true);
if (skipAuthorization)
{
return;
}
if (AuthorizeCore(filterContext.HttpContext) && CheckPermissions(filterContext.HttpContext, controller, action))
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
var cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else
{
HandleUnauthorizedRequest(filterContext);
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
{
filterContext.Result =
new RedirectToRouteResult(
new RouteValueDictionary(new {controller = "Error", action = "Unauthorized"}));
}
}
}
这样,Vivien Chevallier的逻辑完美无缺