基于角色成员资格或数据关系(所有权)的ASP.NET MVC授权

时间:2009-01-15 16:30:07

标签: asp.net-mvc linq-to-sql reflection attributes dynamic-linq

我想在ASP.NET MVC中扩展AuthorizeAttribute,以便它支持用户授权的概念,该授权基于他们的角色成员资格或所讨论数据的“所有权”。我正在使用LINQ2SQL进行数据访问。 asp.net mvc authorization using roles也有类似的问题。

我在想的是将EntityProperty,UserProperty,RouteParameter和JoinTableType参数添加到扩展的AuthorizeAttribute类中。前两个是要检查的连接表中属性的名称。 RouteParameter将是要提取的EntityProperty值匹配的路由参数的名称。我使用当前用户名从用户表中获取用户ID。 JoinTableType参数将是datacontext中包含路由参数值和用户ID必须匹配的Entity和UserProperties的表的类型。

基本思想是伪代码:

 if authorizecore result is true
    user is granted access based on role
 else if user is not authenticated
    redirect to logon
 else if user is related to request
    user is granted access based on relation
 else
    user is not authorized, redirect to not authorized error view

相关测试看起来像:

 result = false
 find the matching user from user name
 find the entity property value in route data
 if user exists and entity property value exists
    get table from context matching join table type
    if table exists
       find row in table matching user id and entity property value
       if row exists
          result = true
       endif
    endif
 endif


 return result

我的问题是如何在构造LINQ查询时使用类型和属性名称?或者我将不得不用object和反思做所有这些。我正在寻找如何使这更容易的想法,所以其他建议也将受到赞赏。我更喜欢使用该属性,而不是直接将检查嵌入到操作中,以使其与我处理其他操作的方式保持一致。

1 个答案:

答案 0 :(得分:1)

我能够使用VS2008示例中的Dynamic Linq扩展以非常合理的方式完成此操作。这是代表上面第二个伪代码样本的代码。它通过了我的初始单元测试,但我需要让它更强大。

用法:

[RoleOrMemberAuthorization( UserTable = "Participants",
                            UserNameProperty = "UserName",
                            UserSelectionProperty = "ParticipantID",
                            JoinTable = "GroupLeaders",
                            EntityProperty = "GroupID",
                            UserEntityProperty = "ParticipantID",
                            RouteParameter = "id",
                            Roles = "SuperUser, ViewGroups" )]

被称为:

else if (IsRelated( filterContext,
                    this.GetTable( dc, this.JoinTable ), 
                    this.GetTable( dc, this.UserTable ) ))
{
    SetCachePolicy( filterContext );
}

相关来源:

protected bool IsRelated( AuthorizationContext filterContext,
                          IQueryable joinTable,
                          IQueryable userTable )
{
    bool result = false;
    try
    {
        object entityIdentifier = filterContext.RouteData
                                               .Values[this.RouteParameter];
        object userIdentifier = this.GetUserIdentifer( filterContext, userTable );
        if (userIdentifier != null && entityIdentifier != null)
        {
            result = joinTable.Where( this.EntityProperty + "=@0 and "
                                      + this.UserEntityProperty + "=@1",
                                      entityIdentifier,
                                      userIdentifier )
                              .Count() > 0;
        }
    }
    catch (NullReferenceException) { }
    return result;
}

private object GetUserIdentifer( AuthorizationContext filterContext,
                                 IQueryable userTable )
{
    string userName = filterContext.HttpContext.User.Identity.Name;

    var query = userTable.Where( this.UserNameProperty + "=@0", userName )
                         .Select( this.UserSelectionProperty );

    object userIdentifer = null;
    foreach (var value in query)
    {
        userIdentifer = value;
        break;
    }
    return userIdentifer;
}

private IQueryable GetTable( DataContext context, string name )
{
    PropertyInfo info = context.GetType().GetProperty( name );
    if (info != null)
    {
        return info.GetValue( context, null ) as IQueryable;
    }
    else
    {
        return null;
    }
}