《复合模式》复杂系统权限控制&设计

       有关系统权限的设计,有诸多的方案&实现方法,甚至不乏有权限相关的通用组件,当然这些可能都局限于特定的业务场景,简单的抑或是复杂的,正如它不可能真正满足所有的需求;本文通过另一个维度来实现系统权限的控制,当然系统方案的采用主要由系统的业务场景决定,整个实现过程相对繁杂,点到为止。


§ 概述

根据业务性的需要,本次设计复合了以下模式:
1、组合
   将权限结构组织成树状结构,使整体-部分的使用拥有一致性
2、外观
   权限组件通过外观,提供给外部、组件调用,组合内部诸多实现接口
3、远程适配
   适配远程接口,转换为内部统一模型,有关远程适配参见:

   http://blog.csdn.net/webwalker/article/details/5652270


§ 名词解释

       会员角色:最高级别用户的权限角色,简称MR

       操作员角色:从属于会员下的权限角色,简称OR

       权限组:以下简称为Role

§ 业务场景

摘要如下:

1、 用户群体

会员(1) à 操作员(*)

一个操作员只从属于一个会员,一个会员拥有不固定数量的操作员

2、 用户类型

已注册未激活用户、未认证用户、签约用户、未签约用户、签约过期用户、操作员用户

不同类别的用户,拥有对应的不固定数量的MR、OR权限集

3、 业务产品

分为需签约、不需要签约两类,签约特定的产品则拥有这类产品下属的特定业务权限,不需要签约的通常默认按以上用户不同阶段默认分配;另外既是签约,则有一定的实效性,签约的过程同样也是系统授权的过程。

4、 用户权限分类

分为系统级别权限和用户自定义级别权限:

系统类权限:需要预先定义的固定权限集合,可在签约时授予,也可在建立、注册用户时默认分配

自定义权限:有限的授权范围内,用户自行设定权限集

5、 权限定义

一个权限Item可包含多个页面(URL),一个权限可从属于多个权限角色,

6、 权限的展现形式

一方面通过菜单的形式显示给用户,另一方面通过页面标签的形式

菜单方式可有两种情况:显示所有菜单(有权限、无权限),用户访问后由系统最终验证是否有权限,另一种为只显示当前用户有权限的菜单。

通过菜单的形式已经确定了权限之间存在着层级关系。

<menu>
  <menuitem id="MGR_ACCOUNT" name="test" url="test.aspx" target="">
    <menuitem id="ACC_HOME" name="test1" url="test1.aspx" target="">
	<url label="test" code="C1000001" actionUrl="/test12.aspx" domainPath="" imgUrl="" target="" />
	</menuitem>
    <menuitem id="ACC_INFO" name="test2" url="/test2.aspx" target="" />
    <menuitem id="ACC_DETAIL" name="test3" url="/test3.aspx" target="" />
    <menuitem id="ACC_SUMMARY" name="test4" url="/test4.aspx" target="" />
    <menuitem id="ACC_REC_DOWN" name="test5" url="/test5.aspx" target="" />
  </menuitem>
</menu>

7、 权限验证

(1)   全局验证URL及用户拥有的访问权限

(2)   验证某个URL中对应页面元素的操作、查看等权限

(3)   验证产品实效性及权限

8、图示


§ 分析设计

1、 数据结构

通常需主要建立:

(1)   基础会员表

(2)   基础权限数据表(系统级别)

(3)   用户自定义权限数据表

(4)   MR关系表

(5)   OR关系表

(6)   签约的产品表

(7)   签约产品和MR角色关系表

2、 权限验证

§ 设计实现

1、权限树结构(结构组合)



权限树内容信息存储:

/// <summary>
    /// 权限树内容信息
    /// (菜单、权限集成)
    /// 结构1
    /// |--菜单
    ///     |--菜单
    ///        |--菜单
    ///            |--URL
    ///                |--页面元素
    /// 结构2:menu、URL权限分开
    /// </summary>
    [Serializable]
    public class AuthSchema
    {
        /// <summary>
        /// 权限ID
        /// </summary>
        public string ID
        {
            get;
            set;
        }

        /// <summary>
        /// 父亲ID
        /// </summary>
        public string ParentID
        {
            get;
            set;
        }

        /// <summary>
        /// 权限名称
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// 权限URL
        /// (绝对路径)
        /// </summary>
        public string Url
        {
            get;
            set;
        }

        /// <summary>
        /// 打开的目标位置
        /// </summary>
        public string Target
        {
            get;
            set;
        }

        /// <summary>
        /// 索引
        /// </summary>
        public string Index
        {
            get;
            set;
        }

        /// <summary>
        /// 排序
        /// </summary>
        public int Sort
        {
            get;
            set;
        }
    }


2、索引树的建立、查找

建立:

    /// <summary>
    /// 树形结构相关处理
    /// </summary>
    internal class TreeService
    {
        #region Init

        static readonly TreeService _Instance = new TreeService();

        /// <summary>
        /// Instance
        /// </summary>
        public static TreeService Instance
        {
            get
            {
                return _Instance;
            }
        }

        #endregion

        #region Tree

        /// <summary>
        /// 权限Schema
        /// </summary>
        AuthSchema s = null;
        /// <summary>
        /// 节点索引计数器
        /// </summary>
        Dictionary<string, int> nodeDic = new Dictionary<string, int>();
        menu[] menuList;

        /// <summary>
        /// 构造树形结构
        /// </summary>
        /// <param name="menus"></param>
        public void Construct()
        {
            menuList = MenuService.Instance.MenuList;

            //Create tree structure
            AuthComposite Tree = new AuthComposite(new AuthSchema
            {
                ID = "-1",
                Name = "Root"
            });

            //construct from root node
            menu[] list = GetNodes("0");

            //Fill Tree
            BuildTree(list, Tree);

            //保存、缓存树结构
            MenuService.Instance.SetMenu(Tree);
        }

        /// <summary>
        /// 根据父节点构造树形结构
        /// </summary>
        /// <param name="menus"></param>
        /// <param name="parentNode"></param>
        public void BuildTree(menu[] menus, AuthCompontent parentNode)
        {
            int index = 0;
            foreach (menu item in menus)
            {
                s = new AuthSchema();
                s.ID = item.id;
                s.Name = item.label;
                s.Url = item.actionUrl;
                s.Sort = item.sort;
                s.Target = item.target;
                s.Index = index.ToString();
                s.ParentID = parentNode.AuthTree.ID;
                AuthComposite node = new AuthComposite(s);
                parentNode.Add(node);

                #region 添加树索引

                AddNodeIndex(item.id);
                if (String.IsNullOrEmpty(parentNode.AuthTree.Index)) s.Index = index.ToString();
                else s.Index = parentNode.AuthTree.Index + AuthConsts.Spliter + GetNodeIndex(s.ParentID);

                #endregion

                if (GetNodes(s.ID).Length > 0)
                {
                    BuildTree(GetNodes(s.ID), node);
                }
            }
        }

        /// <summary>
        /// 根据索引定位 多叉树 结构中的某一元素
        /// </summary>
        /// <param name="c"></param>
        /// <param name="index">索引位置</param>
        /// <returns></returns>
        public AuthCompontent IndexNode(AuthComposite c, int index)
        {
            return c.ChildNodes[index];
        }

        /// <summary>
        /// 添加节点索引
        /// </summary>
        /// <param name="key"></param>
        void AddNodeIndex(string key)
        {
            if (!nodeDic.ContainsKey(key))
                nodeDic.Add(key, 0);
            else
                nodeDic[key] += 1;
        }

        /// <summary>
        /// 获取树节点索引
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        string GetNodeIndex(string key)
        {
            if (!nodeDic.ContainsKey(key))
            {
                return string.Empty;
            }

            string copy = nodeDic[key].ToString();
            nodeDic[key] += 1;

            return copy;
        }

        /// <summary>
        /// 按升序方式,获取父节点下第一层级菜单信息
        /// </summary>
        /// <param name="parentId"></param>
        /// <returns></returns>
        public menu[] GetNodes(string parentId)
        {
            var node = (from m in menuList
                        orderby m.sort
                        where m.parentId == parentId
                        select m).ToArray();

            return node;
        }

        #endregion
    }

查找:

        /// <summary>
        /// 根据索引路径,查找各层级菜单集合
        /// 方便自由组合各菜单界面位置、样式
        /// </summary>
        /// <param name="indexString">待查找的索引路径</param>
        /// <returns></returns>
        public List<AuthCompontent> GetSubMenu(string indexString)
        {
            AuthComposite tree = GetMenu();
            string[] index = indexString.Split(',');
            foreach (string s in index)
            {
                tree = TreeService.Instance.IndexNode(tree, Convert.ToInt32(s)) as AuthComposite;
            }

            return tree.ChildNodes;
        }

3、外部调用


4、权限验证

分别在HttpHandler层提供全局控制、单独的页面访问提供授权验证借口、单独的业务点&URL提供授权验证


§ 应用

1、有关菜单呈现,参见:

http://blog.csdn.net/webwalker/article/details/5338873

2、URL访问时,根据访问页面,自动选中对应的一级、二级菜单

<pages>
  <page id="" url="/test.aspx" path="//MGR_ACCOUNT/ACC_INFO"></page>
  <page id="" url="/test.aspx" path="//MGR_ACCOUNT/ACC_DETAIL"></page>
  <page id="" url="/test.aspx" path="//MGR_ACCOUNT/ACC_SUMMARY"></page>
  <page id="" url="/test.aspx" path="//MGR_ACCOUNT/ACC_SUMMARY"></page>
  <page id="" url="/test.aspx" path="//MGR_ACCOUNT/ACC_REC_DOWN"></page>
</pages>

3、URL访问时,根据访问页面,自动验证当前URL是否需要附加业务逻辑校验
   eg: 验证是否需要实名、是否……
<Root Remark="业务、调用权限配置, 限签约产品相关功能组、自定义功能组">
  <Method Name="" Desc="xx管理" url="/test.aspx" FunctionID="MGR_OPERATOR" IsNeedSign="1"></Method>
  <Method Name="" Desc="xx" url="/test.aspx" FunctionID="MGR_OPERATOR" IsNeedSign="1"></Method>
</Root>

§ 总结

 写的比较仓促,设计部分主要侧重于如何进行权限控制,而对权限数据结构层没有具体涉及,可能也没有啰嗦的必要。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值