加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 通用权限管理器模式

发布时间:2020-12-15 22:08:05 所属栏目:百科 来源:网络整理
导读:我要做的是创建一个使用静态方法的类来管理某些类型资源(NHibernate实体对象)上不同用户类型的权限.具体来说,我想针对对象id检查当前主体(在asp.net MVC项目中),以查看他是否可以查看或编辑实体.我想到的签名如下: PermissionManager.CanViewTEntity(object
我要做的是创建一个使用静态方法的类来管理某些类型资源(NHibernate实体对象)上不同用户类型的权限.具体来说,我想针对对象id检查当前主体(在asp.net MVC项目中),以查看他是否可以查看或编辑实体.我想到的签名如下:

PermissionManager.CanView<TEntity>(object id);

到现在为止我已经完成了以下步骤:

1)这样的界面:

public interface ICanAccessQuery<TAccount,TEntity>
    where TAccount : IAccountOwner
{
    bool CanView(TAccount user,object entityKey);
    bool CanEdit(TAccount user,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
    }
}

3)我的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());
}

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

将为CanEdit定义相同的方法…除了要调用的方法名称之外完全相同.

我要问的是:是否有更好的方法来定义我的想法,以更多的OOP方式?

解决方法

我已经实现了一个更好的解决方案,可能对某些人来说很有趣.

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

public interface ICanAccessQuery<TAccount,TEntity>
    where TAccount : IAccountOwner
    where TEntity : IStoredEntity
{
    bool CanView(TAccount user,TEntity entity);
    bool CanEdit(TAccount user,TEntity entity);
}

我的实体现在实现了一个空接口IStoredEntity来制作约束.

这是一个实施的例子:

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
    }
}

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

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,ability,entity);
        }
        else if (user is TrainingCenter)
        {
            return Can<TrainingCenter,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,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;
    }
}

在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.
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读