如何在 ASP.NET MVC 4 中获取控制器操作的自定义注释属性? [英] How to get custom annotation attributes for a controller action in ASP.NET MVC 4?

查看:25
本文介绍了如何在 ASP.NET MVC 4 中获取控制器操作的自定义注释属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 ASP.NET MVC 中为我的应用程序使用基于权限的授权系统.为此,我创建了一个自定义授权属性

公共类 MyAuthorizationAttribute : AuthorizeAttribute{字符串角色{get;放;}字符串权限{get;放;}}

这样我就可以通过角色或特定权限键对用户进行授权,并带有诸如

之类的操作注释

公共类 UserController : 控制器{[我的授权(角色=ADMIN",权限=USER_ADD")]公共 ActionResult 添加()[我的授权(角色=管理员",权限=用户编辑")]公共操作结果编辑()[我的授权(角色=ADMIN",权限=USER_DELETE")]公共操作结果删除()}

然后我用类似的逻辑(伪代码)覆盖 MyAuthorizationAttribute 类中的 AuthorizeCore() 方法

protected override bool AuthorizeCore(HttpContextBase httpContext){如果(用户未通过身份验证)返回假;if(用户有任何角色角色)返回真;if(用户有权限的任何权限)返回真;返回假;}

到目前为止工作正常.

现在我需要某种扩展方法,以便我可以在视图页面中动态生成操作 url,这些操作 url 将根据给定操作的 MyAuthorization 属性授权逻辑返回操作 url.喜欢

@Url.MyAuthorizedAction("添加", "用户")

如果用户具有管理员角色或具有USER_ADD"权限(如操作的属性中定义),则将 url 返回到用户/添加",否则返回空字符串.

但是在互联网上搜索了几天后,我无法弄清楚.:(

到目前为止,我只找到了这个 安全意识"操作链接?它的工作原理是为操作执行所有操作过滤器,直到操作失败.

这很好,但我认为每次调用 MyAuthorizedAction() 方法时执行所有操作过滤器都是一种开销.此外它也不适用于我的版本(MVC 4 和 .NET 4.5)

我需要做的就是根据授权角色和给定操作的权限检查经过身份验证的用户的角色、权限(将存储在会话中).像下面这样(伪代码)

MyAuthorizedAction(string actionName, string controllerName){ActionObject action = SomeUnknownClass.getAction(actionName, controllerName)MyAuthorizationAttribute attr = action.returnsAnnationAttributes()如果(用户角色包含 attr.Roles 中的任何内容或者用户权限包含任何 attr.Permissions){返回网址以执行操作}返回空字符串}

获取动作属性值的解决方法找了好久,都没有找到足够好的资源.我错过了正确的关键字吗?:/

如果有人能给我提供真正有帮助的解决方案.提前感谢您的解决方案

解决方案

虽然我同意基于权限生成 url 可能不是最佳实践,但如果您仍想继续,您可以使用以下内容找到操作及其属性:

检索操作"方法:这将检索方法信息的集合,因为可能有多个同名的 Controller 类和多个同名的方法,特别是在使用区域时.如果您必须担心这一点,我会将消除歧义留给您.

public static IEnumerableGetActions(字符串控制器,字符串动作){返回 Assembly.GetExecutingAssembly().GetTypes().Where(t =>(t.Name == controller && typeof(Controller).IsAssignableFrom(t))).SelectMany(类型 =>type.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(a => a.Name == action && a.ReturnType == typeof(ActionResult)));}

从 MyAuthorizationAttributes 中检索权限:

public static MyAuthorizations GetMyAuthorizations(IEnumerable actions){var myAuthorization = new MyAuthorizations();foreach (var methodInfo in action){var authorizationAttributes = methodInfo.GetCustomAttributes(typeof (MyAuthorizationAttribute), false).Cast();foreach(authorizationAttributes 中的 var myAuthorizationAttribute){myAuthorization.Roles.Add(MyAuthorizationAttribute.Role);myAuthorization.Permissions.Add(MyAuthorizationAttribute.Permission);}}返回我的授权;}公共类 MyAuthorizations{公共我的授权(){Roles = new List();Permissions = new List();}公共列表<字符串>角色{获取;放;}公共列表<字符串>权限{获取;放;}}

最后是 AuthorizedAction 扩展: 警告:如果给定控制器/操作对有多个匹配项,如果用户被授权使用其中任何一个,这将提供已授权"网址...

public static string AuthorizedAction(this UrlHelper url, string controller, string action){var actions = GetActions(controller, action);var 授权 = GetMyAuthorizations(actions);if(user.Roles.Any(userrole =>authorized.Roles.Any(role =>role == userrole)) ||user.Permissions.Any(userPermission =>authorized.Permissions.Any(permission =>permission == userPermission))){返回 url.Action(controller,action)}返回字符串.空;}

关于根据权限生成网址的说明:
我声明这可能不是最佳实践,因为有很多小事.每个人都可能有自己的相关性级别,具体取决于您的情况.

  • 给人一种试图通过默默无闻来实现安全的印象.如果我不向他们展示网址,他们就不会知道它在那里.
  • 如果您已经在以其他方式检查权限以控制页面的呈现(正如您根据其他地方的评论所做的那样),那么明确不写出 url 是没有意义的.最好不要调用 Url.Action 方法.
  • 如果您尚未根据用户的权限控制页面的呈现,那么简单地为 url 返回空字符串会在您的页面上留下大量损坏或看似损坏的内容.嘿,当我按下这个按钮时,它什么也不做!
  • 它会使测试和调试变得更加复杂:url 不显示是因为权限不对,还是有其他错误?
  • AuthorizedAction 方法的行为似乎不一致.有时返回一个 url,有时返回一个空字符串.

通过操作授权属性控制页面呈现:AuthorizedAction 方法修改为 boolean,然后使用其结果来控制页面呈现.

public static bool AuthorizedAction(this HtmlHelper helper, string controller, string action){var actions = GetActions(controller, action);var 授权 = GetMyAuthorizations(actions);return user.Roles.Any(userrole =>authorized.Roles.Any(role =>role == userrole)) ||user.Permissions.Any(userPermission =>authorized.Permissions.Any(permission =>permission == userPermission))}

然后在您的剃须刀页面中使用它.

@if(Html.AuthorizedAction("User","Add")){<div id='add-user-section'>如果您看到这一点,则您有权添加用户.<form id='add-user-form' submit='@Url.Action("User","Add")'>等等</表单>

}别的 {<其他一些内容/>}

I am working with a permission based authorization system for my app in ASP.NET MVC. For this I have created a custom authorization attribute

public class MyAuthorizationAttribute : AuthorizeAttribute
{
    string Roles {get; set;}
    string Permission {get; set;}
}

so that I can authorize a user by both role or a specific permission key with annotation for actions like

public class UserController : Controller
{
    [MyAuthorization(Roles="ADMIN", Permissions="USER_ADD")]
    public ActionResult Add()

    [MyAuthorization(Roles="ADMIN", Permissions="USER_EDIT")]
    public ActionResult Edit()

    [MyAuthorization(Roles="ADMIN", Permissions="USER_DELETE")]
    public ActionResult Delete()
}

then I override AuthorizeCore() method in MyAuthorizationAttribute class with similar logic(pseudo code)

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
    if(user not authenticated)
        return false;

    if(user has any role of Roles)
        return true;

    if(user has any permission of Permissions)
        return true;

    return false;
}

Up to this is working fine.

Now I need some sort of extension methods so that I can dynamically generate action url in view pages that will return action url based on MyAuthorization attribute authorization logic for the given action. Like

@Url.MyAuthorizedAction("Add", "User")

will return url to "User/Add" if user has admin role or has permission of "USER_ADD" (as defined in attributes for the action) or return empty string otherwise.

But after searching in internet for few days I could not figure it out. :(

So far I have found only this "Security aware" action link? which works by executing all action filters for the action until it fails.

It's nice, but I think it would be an overhead to execute all the action filters for each time I call the MyAuthorizedAction() method. Besides It also didn't work with my version (MVC 4 and .NET 4.5)

What all I need is to check authenticated user's role, permissions (will be stored in session) against authorized role and permission for the given action. Like something as following (pseudo code)

MyAuthorizedAction(string actionName, string controllerName)
{
    ActionObject action = SomeUnknownClass.getAction(actionName, controllerName)
    MyAuthorizationAttribute attr = action.returnsAnnationAttributes()

    if(user roles contains any in attr.Roles 
       or 
       user permissions contains any attr.Permissions)
    {
        return url to action
    }
    return empty string
}

I am searching the solution of getting action attributes value for quite a long time, could not find enough good resources at all. Am I missing out right keywords? :/

If anyone can provide me the solution that would be truly a great help. Thanks in advance for the solutions

解决方案

While I agree that generating urls based on permissions is probably not best practices, if you want to continue anyway you can find the actions and their attributes using these:

Retrieve 'Action' methods: This retrieves a collection of method infos because it is possible to have multiple Controller classes with the same name and multiple methods of the same name in particular with the use of areas. If you have to worry about this I'll leave the disambiguation up to you.

public static IEnumerable<MethodInfo> GetActions(string controller, string action)
{
    return Assembly.GetExecutingAssembly().GetTypes()
           .Where(t =>(t.Name == controller && typeof(Controller).IsAssignableFrom(t)))
           .SelectMany(
                type =>
                type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
                    .Where(a => a.Name == action && a.ReturnType == typeof(ActionResult))
             );

}

Retrieve Permissions from MyAuthorizationAttributes:

public static MyAuthorizations GetMyAuthorizations(IEnumerable<MethodInfo> actions)
{
    var myAuthorization = new MyAuthorizations();
    foreach (var methodInfo in actions)
    {
        var authorizationAttributes =  methodInfo
                .GetCustomAttributes(typeof (MyAuthorizationAttribute), false)
                .Cast<MyAuthorizationAttribute>();
        foreach (var myAuthorizationAttribute in authorizationAttributes)
        {
            myAuthorization.Roles.Add(MyAuthorizationAttribute.Role);
            myAuthorization.Permissions.Add(MyAuthorizationAttribute.Permission);
        }
    }
    return myAuthorization;
}
public class MyAuthorizations
{
    public MyAuthorizations()
    {
        Roles = new List<string>();
        Permissions = new List<string>();
    }
    public List<string> Roles { get; set; }
    public List<string> Permissions { get; set; }
}

Finally the AuthorizedAction extension: warning:If you do have more than one match for a given controller/action pair this will give the 'authorized' url if the user is authorized for any of them...

public static string AuthorizedAction(this UrlHelper url, string controller, string action)
{
    var actions = GetActions(controller, action);
    var authorized = GetMyAuthorizations(actions);
    if(user.Roles.Any(userrole => authorized.Roles.Any(role => role == userrole)) ||
       user.Permissions.Any(userPermission => authorized.Permissions.Any(permission => permission == userPermission)))
    {
        return url.Action(controller,action)
    }
    return string.empty;
}

A Note on Generating Urls Based on Permissions:
I state this is probably not best practice because of many little things. Each may have their own level of relevance depending on your situation.

  • Gives the impression of trying to achieve security through obscurity. If I don't show them the url, they won't know it is there.
  • If you are already checking permissions in other ways to control the rendering of the page (as it appears you are doing based on your comments elsewhere) this explicitly not writing the url out is pointless. Better to not even call the Url.Action method.
  • If you are not already controlling the rendering of the page according to the user's permissions, simply returning empty string for urls will leave a lot of broken or seemingly broken content on your pages. Hey this button doesn't do anything when I press it!
  • It can make testing and debugging more complicated: Is the url not showing up because the permissions are not right, or is there another bug?
  • The behavior of the AuthorizedAction method seems inconsistent. Returns a url sometimes, and an empty string other times.

Controlling Page Rendering Via Action Authorization attributes: Modify the AuthorizedAction method to be a boolean, then use the result of that to control page rendering.

public static bool AuthorizedAction(this HtmlHelper helper, string controller, string action)
{
    var actions = GetActions(controller, action);
    var authorized = GetMyAuthorizations(actions);
    return user.Roles.Any(userrole => authorized.Roles.Any(role => role == userrole)) ||
       user.Permissions.Any(userPermission => authorized.Permissions.Any(permission => permission == userPermission))
}

Then use this in your razor pages.

@if(Html.AuthorizedAction("User","Add")){
   <div id='add-user-section'>
        If you see this, you have permission to add a user.
        <form id='add-user-form' submit='@Url.Action("User","Add")'>
             etc
        </form>
   </div>
}
else {
  <some other content/>

}

这篇关于如何在 ASP.NET MVC 4 中获取控制器操作的自定义注释属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆