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

发布于 2024-07-12 01:56:18 字数 1442 浏览 12 评论 0原文

我想扩展 ASP.NET MVC 中的 AuthorizeAttribute,以便它支持用户基于其角色成员身份或相关数据的“所有权”进行授权的概念。 我使用 LINQ2SQL 进行数据访问。 asp.net mvc 授权使用角色有一个类似的问题。

我的想法是将 EntityProperty、UserProperty、RouteParameter 和 JoinTableType 参数添加到我的扩展 AuthorizeAttribute 类中。 前两个是连接表中要检查的属性的名称。 RouteParameter 将是要提取的路由参数的名称,以匹配 EntityProperty 的值。 我将使用当前用户名从用户表中获取用户 ID。 JoinTableType 参数将是数据上下文中表的类型,其中包含路由参数值和用户 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 和反射来完成所有这些工作。 我真的在寻找如何使这变得更容易的想法,因此其他建议也将受到赞赏。 我更愿意使用该属性,而不是将检查直接嵌入到操作中,以使其与我处理其他操作的方式保持一致。

I'd like to extend the AuthorizeAttribute in ASP.NET MVC so that it supports the concept of a user's authorization being based on their role membership OR "ownership" of the data in question. I'm using LINQ2SQL for data access. There is a similar question at asp.net mvc authorization using roles.

What I'm thinking is adding EntityProperty, UserProperty, RouteParameter, and JoinTableType parameters to my extended AuthorizeAttribute class. The first two would be the names of the properties in the join table to check. The RouteParameter would be the name of the route parameter to extract for the value of the EntityProperty to match. I'd obtain the user id from the user's table using the current user name. The JoinTableType parameter would be the type of the table in the datacontext that contains the Entity and UserProperties that the route parameter value and user id must match.

The basic idea is, in pseudocode:

 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

The is related test would look like:

 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

My question is how do I use the type and property names in constructing the LINQ query? Or am I going to have to do all this with object and reflection. I'm really searching for ideas on how to make this easier so other suggestions would be appreciated as well. I'd prefer to use the attribute rather than embed the checking directly in the action to keep this consistent with how I handle my other actions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

街角卖回忆 2024-07-19 01:56:18

我能够使用 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;
    }
}

I was able to use the Dynamic Linq extensions from the VS2008 samples to do this in a pretty reasonable fashion. Here's the code representing the second pseudocode sample from above. It passes my initial unit test, but I'll need to make it more robust.

Usage:

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

Called as:

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

Relevant source:

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文