gpt4 book ai didi

c# - 我可以为给定的 ActionResult 手动调用 ClaimsAuthorizationManager 吗?

转载 作者:太空宇宙 更新时间:2023-11-03 17:52:25 24 4
gpt4 key购买 nike

我有一个 ASP.NET MVC Web 应用程序,它通过 WIF 和 Thinktecture.IdentityModel 使用基于声明的授权.但是,不仅仅是拦截未经授权的请求,我想修剪我的导航菜单以仅显示当前用户可访问的链接。

我最初的想法是接受一个 Action 列表(ActionResult、ActionLink、路由值字典,还不太确定)并执行我的自定义 ClaimsAuthorizationManger.CheckAccess常规。为了做到这一点,我需要生成一个 AuthorizationContext,但是,我不确定创建上下文的框架实用程序(或者最好是抽象)是否可以访问。有谁知道这是否可能?或者,如果我对这一切都错了,你有什么建议?

谢谢,节日快乐!

最佳答案

正如 Dominick Baier 所指出的,我能够使用 Thinktecture.IdentityModel 创建一个不错的解决方案. I've posted the code in a Gist .这个特定的功能似乎没有记录,但我能够将相关博客文章和文档中的点点滴滴拼凑起来。我会很感激任何反馈,因为我确信 IdentityModel 或 WIF 的某些角落可以减轻我的代码的影响。也就是说,这运行良好。让我们从一个示例用法开始:

Razor

// This would likely be a partial view with per-user output caching
@Html.BuildNavigation(new List<NavigationItem>
{
new NavigationItem("Production", MVC.Production.Home.Index()),
new NavigationItem("Inventory", MVC.Inventory.Home.Index()),
new NavigationItem("Quality Control", MVC.QualityControl.Home.Index()),
new NavigationItem("Customers", MVC.Sales.Home.Index()),
new NavigationItem("Vendors", MVC.Vendors.Companies.Index()),
})

如下图所示, BuildNavigation辅助函数将呈现 <ul>包含用户有权查看的所有导航项。如果您想知道 MVC.{Area}.{Controller}.{Action}()声明,这些是 T4MVC helper 。没有必要使用 T4MVC。 NavigationItem提供接受 ActionResult 的覆盖为了方便。实际上, NavigationItem仅由 string 组成显示和 RouteValueDictionary ( see the Gist)。

HTML 助手扩展

为了利用 Thinktecture.IdentityModel 提供的现有授权实用程序,您必须创建一个 RequestContext .请注意,我们构建了 RouteData来自 RouteValueDictionary然后创建一个新的 RequestContext对于授权助手。 重要的旁注:如果您在 MVC 项目中使用区域,就像我一样,AddNamespaceInfo功能最重要。 如果您在不同的区域有重复的 Controller 名称,那么 Controller 工厂将知道如何访问正确的名称。否则,您将收到异常。
public static class NavigationHelper
{
public static MvcHtmlString BuildNavigation(this HtmlHelper htmlHelper, IEnumerable<NavigationItem> navigationItems)
{
var container = new TagBuilder("ul");
container.MergeAttribute("id", "menu");
var innerHtmlBuilder = new StringBuilder();
foreach (var item in navigationItems.Where(item => IsAuthorized(htmlHelper, item.RouteValueDictionary)))
{
innerHtmlBuilder.Append(
new TagBuilder("li")
{
InnerHtml = htmlHelper.ActionLink(
item.LinkText,
item.RouteValueDictionary["action"] as string,
item.RouteValueDictionary["controller"] as string,
item.RouteValueDictionary, null).ToHtmlString()
});
}
container.InnerHtml = innerHtmlBuilder.ToString();
return new MvcHtmlString(container.ToString());
}

private static bool IsAuthorized(this HtmlHelper htmlHelper, RouteValueDictionary routeValues)
{
var routeData = BuildRouteData(htmlHelper.RouteCollection, routeValues);
var context = BuildRequestContext(htmlHelper, routeData);
return ClaimsAuthorizationHelper.CheckAccess(context);
}

private static RouteData BuildRouteData(IEnumerable<RouteBase> routeCollection, RouteValueDictionary routeValues)
{
object controllerValue;
routeValues.TryGetValue("controller", out controllerValue);
var controllerName = controllerValue as string;

object actionValue;
routeValues.TryGetValue("action", out actionValue);
var actionName = actionValue as String;

object areaValue;
routeValues.TryGetValue("area", out areaValue);
var areaName = areaValue as String ?? "";

var routeData = new RouteData();
routeData.Values.Add("action", actionName);
routeData.Values.Add("controller", controllerName);
routeData.Values.Add("area", areaName);
AddNamespaceInfo(routeData, routeCollection, areaName, controllerName, actionName);

return routeData;
}

private static RequestContext BuildRequestContext(this HtmlHelper htmlHelper, RouteData routeData)
{
var claimsPrincipal = htmlHelper.ViewContext.HttpContext.User as ClaimsPrincipal;
var requestContext = new RequestContext(htmlHelper.ViewContext.HttpContext, routeData);
requestContext.HttpContext.User = claimsPrincipal;

return requestContext;
}

private static void AddNamespaceInfo(RouteData routeData, IEnumerable<RouteBase> routeCollection, string areaName, string controllerName, string actionName)
{
var route = routeCollection.GetRoute(areaName, controllerName, actionName);

if (route != null)
{
routeData.DataTokens.Add("Namespaces", route.DataTokens["Namespaces"]);
}
}
}

Thinktecture.IdentityModel ClaimsAuthorizeAttribute Wrapper

我遇到的另一个绊脚石是 ClaimsAuthorizeAttribute 的封闭性。 .这是我怀疑通过更深入地了解 WIF 可能会消除的一个领域。然而,当时,我已经为 ClaimsAuthorizeAttribute 创建了一个包装器。这允许我将属性转换为声明。
public class ClaimsAuthorizeAttribute : Thinktecture.IdentityModel.Authorization.Mvc.ClaimsAuthorizeAttribute
{
private readonly string _action;
private readonly string[] _resources;

public ClaimsAuthorizeAttribute(string action, params string[] resources)
:base(action, resources)
{
_action = action;
_resources = resources;
}

public IEnumerable<Claim> GetClaims()
{
return _resources.Select(r => new Claim(_action, r));
}
}

理赔授权助手

最后,这里是 ClaimsAuthorizationHelper它负责解析必要的 Controller 和操作方法,检索资源声明,并调用 ClaimsAuthorization Thinktecture IdentityModel 提供的实用程序。
public static class ClaimsAuthorizationHelper
{
public static bool CheckAccess(RequestContext requestContext)
{
var routeData = requestContext.RouteData;
var controllerName = routeData.Values["controller"] as string;
var actionName = routeData.Values["action"] as string;

var controller = GetControllerByName(requestContext, controllerName);
var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
var controllerContext = new ControllerContext(requestContext, controller);
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);

var resourceClaims = actionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof (ClaimsAuthorizeAttribute), false)
.Cast<ClaimsAuthorizeAttribute>()
.SelectMany(auth => auth.GetClaims()).ToList();

resourceClaims.AddRange(actionDescriptor.GetCustomAttributes(typeof(ClaimsAuthorizeAttribute), false).Cast<ClaimsAuthorizeAttribute>()
.SelectMany(c => c.GetClaims()));

var hasAccess = ClaimsAuthorization.CheckAccess(actionName, resourceClaims.ToArray());
return hasAccess;
}

public static ControllerBase GetControllerByName(RequestContext requestContext, string controllerName)
{
var factory = ControllerBuilder.Current.GetControllerFactory();
var controller = factory.CreateController(requestContext, controllerName);
if (controller == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The current controller factory, \"{0}\", did not return a controller for the name \"{1}\".", factory.GetType(), controllerName));
}

return (ControllerBase)controller;
}
}

其他代码

为简洁起见,还有一些其他的助手和类被省略了。请 see the Gist for the full code .

关于c# - 我可以为给定的 ActionResult 手动调用 ClaimsAuthorizationManager 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20764053/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com