基于控制器/操作的MVC UI修剪授权属性
我有一个安全应用程序,每个操作都有一个Authorize
属性。基于控制器/操作的MVC UI修剪授权属性
[Authorize(Roles = "Role1,Role2")]
public ActionResult MyAction(int id)
{
return View();
}
在我的用户界面中,我有这些控制器/操作的链接。我想创建一个链接一个自定义的HtmlHelper接受控制器和动作名称:
@Html.SecuredLink("Click Me", "MyAction", "MyController");
,这将决定耐候来表现自己或不基于用户是否有权限给定的行动:
public static MvcHtmlString SecuredLink(this HtmlHelper helper, string text, string action, string controller)
{
var userId = Membership.GetUserId();
var userHasRightsToThisAction = IsActionAccessibleToUser(helper.ViewContext.RequestContext.HttpContext, controller, action); // <- How would this work?
if (userHasRightsToThisAction)
{
// Render Link
// ...
}
}
我一直无法找到一种方法来轻松测试授权状态代码中的操作。
好的找到了解决方案。围绕我知道做安全修整的MvcSiteMap挖后,我发现这篇文章吧:
我用了一下这个代码,稍加修改,创建,让我想要的结果的方法:
/// <summary>
/// Determine if a controller/action is accessible for a user
/// </summary>
/// <param name="context">Current HttpContext</param>
/// <param name="controllerName">Target controller</param>
/// <param name="actionName">Target action</param>
/// <returns>True/false if the action is accessible</returns>
public static bool IsActionAccessibleToUser(HttpContextBase context, string controllerName, string actionName)
{
// Find current handler
MvcHandler handler = context.Handler as MvcHandler;
if (handler != null)
{
// try to figure out the controller class
IController controller = null;
try
{
controller = ControllerBuilder.Current.GetControllerFactory().CreateController(handler.RequestContext, controllerName);
}
catch (System.Web.HttpException e)
{
throw new Exception("The controller '" + controllerName + "Controller' was not found.", e);
}
// Find all AuthorizeAttributes on the controller class and action method
object[] controllerAttributes = controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true);
object[] actionAttributes = controller.GetType().GetMethod(actionName).GetCustomAttributes(typeof(AuthorizeAttribute), true);
// No attributes, then the action is open to all
if (controllerAttributes.Length == 0 && actionAttributes.Length == 0) return true;
// Find out current principal
IPrincipal principal = handler.RequestContext.HttpContext.User;
// Do we pass the roles for the controller?
string roles = "";
if (controllerAttributes.Length > 0)
{
AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute;
roles = attribute.Roles;
if (!PassRoleValidation(principal, roles)) return false;
}
// Do we pass the roles for the action?
if (actionAttributes.Length > 0)
{
AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute;
roles = attribute.Roles;
if (!PassRoleValidation(principal, roles)) return false;
}
return true;
}
return false;
}
private static bool PassRoleValidation(IPrincipal principal, string roles)
{
// no roles, then all we need to be is authenticated
if (string.IsNullOrEmpty(roles) && principal.Identity.IsAuthenticated) return true;
string[] roleArray = roles.Split(',');
// if role contains "*", it's open to all
if (roleArray.Any(role => role == "*")) return true;
// Determine if the current user is allowed to access the current node
if (roleArray.Any(principal.IsInRole)) return true;
return false;
}
如果您的控制器有两个具有相同名称的操作(例如,一个用于POST,另一个用于GET请求),则这不起作用。 'controller.GetType()。GetMethod(actionName)'会抛出'AmbiguousMatchException'。 –
好,快速和灰尘的解决方案:
准备一个功能构建的URL服务器端
这样的事情可能会是最好的选择:
public static string GetUrl(string Action, string Controller, object RouteValues) {
UrlHelper Url = new UrlHelper(HttpContext.Current.Request.RequestContext);
return Url.Action(Action, Controller, RouteValues);
}
在你的助手,获得用户身份验证信息并返回构建的url或string.Empty。
public static string SecureLink(this HtmlHelper helper, string Action, string Controller, object RouteValues)
{
YourUserObject LoggedUser = /* Whatever you need to obtain your UserId */
if (LoggedUser.IsSuperUser) {
return GetUrl(Action, Controller, RouteValues);
}
return string.empty;
}
如果你的结果是HTML编码只是使用MvcHtmlString代替字符串作为返回值。 否则要小心,您可能需要使用@Html.Raw
来发射它。
PS:很明显,我还没有完全加代<a href .../>
在此示例代码,是由你决定哪些参数是需要(将其添加到助手的签名),我通常复制其他@Html
佣工的签名(所以名称,值和HtmlAttributes的列表)。
你说什么“LoggedUser.IsSuperUser”,我想根据每个MVC Action的Authorize decortations来确定权限。这是我正在努力的部分。所以如果Action有“授权(Roles =”Associate“),我想以某种方式确定它,并让系统告诉我,如果当前用户是”Associate“,或者更好,他们是否有资格执行Action。 – CodeGrue
我真的怀疑你可以从Helper中访问Attributes属性,在执行Action之后,Helper被执行用于渲染View。你可以尝试构建一个javascript(客户端)工件来启用/禁用Links,但是那样会恕我直言,在这种情况下最好让链接点击并处理未授权的服务器端。 – BigMike
好吧,我正在测试一个不同的控制器/动作,不一样。所以我在A/B上,链接转到A/C,所以我想问A/C,如果用户有权去那里。所以我并不试图从内部测试我所处理的控制器/操作。 – CodeGrue
你能澄清为什么不能正常渲染链接,然后在自定义的AuthorizeAttribute中重定向?不过,我认为你应该首先查看User.Identity对象,然后确定授权名称,然后决定是否渲染。 – BigMike
我不希望用户能够点击该链接,如果他们没有对其导致的页面的权利。 – CodeGrue