如何在ASP.NET MVC 4中获取控制器操作的自定义注释属性?

Bur*_*din 10 c# asp.net-mvc razor

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

public class MyAuthorizationAttribute : AuthorizeAttribute
{
    string Roles {get; set;}
    string Permission {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以通过角色或具有注释的特定权限密钥来授权用户

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()
}
Run Code Online (Sandbox Code Playgroud)

然后我在MyAuthorizationAttribute类中重写具有类似逻辑的AuthorizeCore()方法(伪代码)

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;
}
Run Code Online (Sandbox Code Playgroud)

到目前为止工作正常.

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

@Url.MyAuthorizedAction("Add", "User")
Run Code Online (Sandbox Code Playgroud)

如果用户具有admin角色或具有"USER_ADD"权限(如操作的属性中所定义),则将URL返回到"User/Add",否则返回空字符串.

但在互联网上搜索了几天后,我无法理解.:(

到目前为止,我只找到了这个"安全感知"动作链接?通过执行操作的所有操作过滤器直到失败为止.

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

我需要的是检查经过身份验证的用户的角色,权限(将存储在会话中)与授权角色和给定操作的权限.喜欢以下内容(伪代码)

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
}
Run Code Online (Sandbox Code Playgroud)

我正在寻找获取动作属性值的解决方案很长一段时间,根本找不到足够的好资源.我错过了正确的关键字吗?:/

如果有人能为我提供真正有用的解决方案.在此先感谢您的解决方案

Mr.*_*dor 15

虽然我同意根据权限生成网址可能不是最佳做法,但如果您仍想继续,可以使用以下方法找到操作及其属性:

检索'Action'方法: 这将检索方法信息的集合,因为可以使用多个具有相同名称的Controller类和多个同名方法,特别是使用区域.如果你不得不担心这一点,我会把消除歧义留给你.

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))
             );

}
Run Code Online (Sandbox Code Playgroud)

从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; }
}
Run Code Online (Sandbox Code Playgroud)

最后是AuthorizedAction扩展: 警告:如果你确实有一个给定控制器/动作对的匹配,如果用户被授权任何一个,那么这将给出'授权'url ...

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;
}
Run Code Online (Sandbox Code Playgroud)

关于根据权限生成URL的注意事项:
我声明这可能不是最佳实践,因为有许多小事情.根据您的具体情况,每个人可能都有自己的相关程度.

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

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

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))
}
Run Code Online (Sandbox Code Playgroud)

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

@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/>

}
Run Code Online (Sandbox Code Playgroud)