C# 通用权限管理器模式

C# 通用权限管理器模式,c#,asp.net-mvc,oop,C#,Asp.net Mvc,Oop,我要做的是用静态方法创建一个类来管理不同用户类型对某些类型的资源(NHibernate实体对象)的权限。具体来说,我想对照对象id检查当前主体(在asp.net MVC项目中),看看他是否可以查看或编辑实体。我心目中的签名如下: PermissionManager.CanView<TEntity>(object id); PermissionManager.CanView(对象id); 现在我已经完成了以下步骤: 1) 这样的界面: public interface ICanAc

我要做的是用静态方法创建一个类来管理不同用户类型对某些类型的资源(NHibernate实体对象)的权限。具体来说,我想对照对象id检查当前主体(在asp.net MVC项目中),看看他是否可以查看或编辑实体。我心目中的签名如下:

PermissionManager.CanView<TEntity>(object id);
PermissionManager.CanView(对象id);
现在我已经完成了以下步骤:

1) 这样的界面:

public interface ICanAccessQuery<TAccount, TEntity>
    where TAccount : IAccountOwner
{
    bool CanView(TAccount user, object entityKey);
    bool CanEdit(TAccount user, object entityKey);
}
公共接口访问查询
where TAccount:IAccountOwner
{
boolcanview(TAccount用户,object entityKey);
boolcanedit(TAccount用户,object entityKey);
}
2) 一些实现如下所示:

public class TeacherCanAccessCourseReportsQuery : ICanAccessQuery<Teacher, CourseReport>
{
    public bool CanView(Teacher user, object entityKey)
    {
        var predicate = PredicateBuilder.Create<CourseReport>(x => x.Id == (long)entityKey);

        var conditions = PredicateBuilder.Create<CourseReport>(x => x.Teacher.Id == user.Id);
        conditions = conditions.Or(x => x.Teacher.Tutor.Id == user.Id);
        conditions = conditions.Or(x => x.CoachingTeachers.Any(t => t.Id == user.Id));

        predicate = predicate.And(conditions);

        return RepositoryProvider.Get<CourseReport>().Count(predicate) > 0;
    }

    public bool CanEdit(Teacher user, object entityKey)
    {
        // similar implementation
    }
}
公共类教师可以访问资源报告:ICANCAccessQuery
{
公共bool CanView(教师用户、对象实体键)
{
var predicate=PredicateBuilder.Create(x=>x.Id==(long)entityKey);
var conditions=PredicateBuilder.Create(x=>x.Teacher.Id==user.Id);
conditions=conditions.Or(x=>x.Teacher.Tutor.Id==user.Id);
conditions=conditions.Or(x=>x.CoachingTeachers.Any(t=>t.Id==user.Id));
谓词=谓词和(条件);
返回RepositoryProvider.Get().Count(谓词)>0;
}
公共bool CanEdit(教师用户、对象实体键)
{
//类似的实现
}
}
3) my PermissionManager类中的静态Configure()方法,将在Global.asax中调用:

public static IDictionary<string, object> _permissions = new Dictionary<string, object>();

public static void Configure()
{
    _permissions.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
}
publicstaticidictionary\u permissions=newdictionary();
公共静态void Configure()
{
_添加(typeof(Teacher).Name+typeof(CourseReport).Name,新教师可以访问coursereportsquery());
}
4) 在PermissionManager类中:

public static bool CanView<TEntity>(object primaryKey, params string[] enabledRoles)
{
    var accounts = RepositoryProvider.Get<Account, AccountRepository>();
    var principal = Thread.CurrentPrincipal as MyCustomPrincipal;

    if (enabledRoles.Any(r => principal.IsInRole(r)))
        return true;

    IAccountOwner user = accounts.GetUser(principal.AccountId);

    var can = false;
    var @switch = new Dictionary<Type, Action> {
            { typeof(Teacher), () => can = CanView<Teacher, TEntity>(user as Teacher, primaryKey) },
            { typeof(TrainingCenter), () => can = CanView<TrainingCenter, TEntity>(user as TrainingCenter, primaryKey) }
    };

    @switch[user.GetType()]();

    return can;
}

private static bool CanView<TAccount, TEntity>(TAccount user, object primaryKey)
        where TAccount : IAccountOwner
{
    var key = typeof(TAccount).Name + typeof(TEntity).Name;
    if (_permissions.ContainsKey(key))
    {
        return (((ICanAccessQuery<TAccount, TEntity>)_permissions[key]).CanView(user, primaryKey);
    }
    return false;
}
publicstaticboolcanview(对象主键,参数字符串[]启用项)
{
var accounts=RepositoryProvider.Get();
var principal=Thread.CurrentPrincipal作为MyCustomPrincipal;
if(enabledRoles.Any(r=>principal.IsInRole(r)))
返回true;
IAccountOwner user=accounts.GetUser(principal.AccountId);
var=false;
var@switch=新字典{
{typeof(Teacher),()=>can=CanView(用户作为教师,primaryKey)},
{typeof(TrainingCenter),()=>can=CanView(用户作为TrainingCenter,primaryKey)}
};
@开关[user.GetType()]();
还可以;
}
私有静态bool CanView(TAccount用户,对象主键)
where TAccount:IAccountOwner
{
var key=typeof(TAccount).Name+typeof(TEntity).Name;
if(_permissions.ContainsKey(key))
{
返回(((ICanAccessQuery)_permissions[key]).CanView(user,primaryKey);
}
返回false;
}
除了要调用的方法名之外,CanEdit将定义相同的方法…完全相同


我想问的是:有没有更好的方法来定义我的想法,用一种更面向对象的方式?

我已经实现了一个更好的解决方案,可能会让人感兴趣

这是“我可以访问吗?”查询的界面:

最后,我的新的
AuthorizationProvider
类(从PermissionManager更改了名称,不喜欢它):

公共类授权提供程序
{
公共枚举能力
{
看法
编辑
};
私有静态IDictionary_authorizations=new Dictionary();
//此方法应在应用程序引导时调用,例如asp.net应用程序中的Global.asax
公共静态void Configure()
{
_添加(typeof(Teacher).Name+typeof(CourseReport).Name,新教师可以访问coursereportsquery());
_添加(typeof(Teacher).Name+typeof(Order).Name,新的TeacherCanAccessOrdersQuery());
//其他规则用户类型实体类型
}
//我可以查看主键为X的实体吗?
公共静态布尔CanI(能力、对象实体键)
在哪里有触角:历史
{
TEntity entity=RepositoryProvider.Get().Load(entityKey);
返回CanI(能力、实体、AccountRoles.Admin);
}
//我可以查看实体吗(如果我有一个特定的角色,我当然可以)?
公共静态布尔CanI(能力能力、tenty实体、参数字符串[]authorizedRoles)
在哪里有触角:历史
{
var principal=Thread.CurrentPrincipal作为MyCustomPrincipal;
if(authorizedRoles.Any(r=>principal.IsInRole(r)))
返回true;
var user=RepositoryProvider.Get().GetUser(principal.AccountId);
//我的系统只有两种类型的用户
如果(用户是教师)
{
返回Can(用户作为教师、能力、实体);
}
否则如果(用户是培训中心)
{
返回Can(用户作为培训中心、能力、实体);
}
返回false;
}
///用户X(查看|编辑)实体Y可以吗?
///经过一些反思,我调用了所需的方法,这样我就可以在查询中添加“能力”
///接口及其实现,而不改变该类。
公共静态bool Can(TAccount用户、能力、能力实体)
where TAccount:IAccountOwner
在哪里有触角:历史
{
var key=typeof(TAccount).Name+typeof(TEntity).Name;
如果(_authorizations.ContainsKey(关键))
{
var查询=(ICanAccessQuery)_授权[键];
string methodName=“Can”+ability.ToString();
var method=typeof(ICanAccessQuery).GetMethod(methodName);
调用(查询,新对象[]{user,entity});
}
返回false;
}
}
在asp.net mvc控制器中使用的示例:

public ActionResult Details(long? id)
{
    if (!id.HasValue)
        return new EmptyResult();

    if (!AuthorizationProvider.CanI<CourseReport>(AuthorizationProvider.Abilities.View, id.Value))
        return RedirectToAccessDenied();

     // etc.
}
public ActionResult详细信息(long?id)
{
如果(!id.HasValue)
返回新的EmptyResult();
if(!AuthorizationProvider.CanI(AuthorizationProvider.Abilities.View,id.Value))
返回RedirectToAccessDenied();
//等等。
}
如果您
public class TeacherCanAccessOrdersQuery : ICanAccessQuery<Teacher, Order>
{
    public bool CanView(Teacher user, Order entity)
    {
        var predicate = PredicateBuilder.Create<Order>(x => x.Id == entity.Id && x => x.Account.Id == user.Account.Id);
        return RepositoryProvider.Get<Order>().Count(predicate) > 0;
    }

    public bool CanEdit(Teacher user, Order entity)
    {
        // similar implementation
    }
}
public class AuthorizationProvider
{
    public enum Abilities
    {
        View,
        Edit
    };

    private static IDictionary<string, object> _authorizations = new Dictionary<string, object>();

    // this method should be called at application bootstrap, such as Global.asax in an asp.net app
    public static void Configure()
    {
        _authorizations.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
        _authorizations.Add(typeof(Teacher).Name + typeof(Order).Name, new TeacherCanAccessOrdersQuery());
        // other rules user type-entity type
    }

    // Can I view entity with primary key X?
    public static bool CanI<TEntity>(Abilities ability, object entityKey)
        where TEntity : IStoredEntity
    {
        TEntity entity = RepositoryProvider.Get<TEntity>().Load(entityKey);
        return CanI<TEntity>(ability, entity, AccountRoles.Admin);
    }

    // Can I view entity (and if I have a specific role, I surely can)?
    public static bool CanI<TEntity>(Abilities ability, TEntity entity, params string[] authorizedRoles)
        where TEntity : IStoredEntity
    {
        var principal = Thread.CurrentPrincipal as MyCustomPrincipal;

        if (authorizedRoles.Any(r => principal.IsInRole(r)))
            return true;

        var user = RepositoryProvider.Get<Account, AccountRepository>().GetUser(principal.AccountId);

        // my system has only two types of users
        if (user is Teacher)
        {
            return Can<Teacher, TEntity>(user as Teacher, ability, entity);
        }
        else if (user is TrainingCenter)
        {
            return Can<TrainingCenter, TEntity>(user as TrainingCenter, ability, entity);
        }
        return false;
    }

    /// Can user X (view|edit) entity Y?
    /// With some reflection I call the needed method. In this way I can add "abilities" to my ICanAccessQuery
    /// interface and its implementations without altering this class.
    public static bool Can<TAccount, TEntity>(TAccount user, Abilities ability, TEntity entity)
        where TAccount : IAccountOwner
        where TEntity : IStoredEntity
    {
        var key = typeof(TAccount).Name + typeof(TEntity).Name;
        if (_authorizations.ContainsKey(key))
        {
            var query = (ICanAccessQuery<TAccount, TEntity>)_authorizations[key];
            string methodName = "Can" + ability.ToString();

            var method = typeof(ICanAccessQuery<TAccount, TEntity>).GetMethod(methodName);
            return (bool)method.Invoke(query, new object[] { user, entity });
        }
        return false;
    }
}
public ActionResult Details(long? id)
{
    if (!id.HasValue)
        return new EmptyResult();

    if (!AuthorizationProvider.CanI<CourseReport>(AuthorizationProvider.Abilities.View, id.Value))
        return RedirectToAccessDenied();

     // etc.
}