﻿一、概述
    权限系统提供了统一的登录验证及权限验证机制，在具体的业务系统当中，您必须使用权限系统
    所提供的接口函数来实现登录验证和权限验证，如果您自定义登录验证了逻辑，则权限系统中的
    很多功能将无法再使用。

二、使用约定

    1、权限系统使用Session作为登录身份验证机制，所以必须保证运行环境中Session可用。
    2、要求业务系统中的需要进行身份验证和权限验证的页面必须从统一的一个页面基类（例如：
       PageBase.cs）继承，并且该基类必须有以下事件：
       
       1）protected override void OnInit(EventArgs e);              //页面初始化事件；
       2）public void Page_PreRender(object sender, EventArgs e)    //页面预呈现事件；
       
       业务系统在 1）OnInit 事件中执行登录身份验证及页面访问权限验证，在 2）Page_PreRender 事件中执行按钮执行权限验证。

三、如何引用权限系统的程序集及页面

    将SysMembership文件夹（该文件夹下包含权限系统的所有文件）放置到业务系统/Controls/下，然后在SysMembership下建立文本
    文件ver.txt，在该文本文件中记录下当前SysMembership的版本号。
    
    在UI项目中添加对权限系统程序集的引用，具体要引用的文件为：
    /Controls/SysMembership/bin/Chaokers.CN.SysMembership.API.dll
    /Controls/SysMembership/bin/Chaokers.CN.SysMembership.Components.dll
    /Controls/SysMembership/bin/Chaokers.CN.SysMembership.Web.dll
    /Controls/SysMembership/bin/Chaokers.CN.SysMembership.WebComponents.dll
    
    如果业务系统的PageBase类在另外一个单独的项目（例如：WebRoot）中，则该项目也要添加对权限系统程序集的引用，引用的文件
    与上面相同。

    
四、登录代码接入  
    
    
    登录部分的业务逻辑代码如下：
    
    ……
    
    string userID       = "hanxiao"; //用户名，实际使用中取输入值；
    string pasword      = "hanxiao"; //密码，实际使用中取输入值；
    

    /*****以下代码为必须内容，如果有其它业务要处理，建议放在该段代码块之外.BEGIN***********************/

    //检测用户名和密码是否正确；
    if (!Chaokers.CN.SysMembership.API.CheckUserIDAndPassword(userID, pasword))
    {
        Response.Write("用户名或密码错误！");
        return;
    }

    string roleName     = Chaokers.CN.SysMembership.API.GetUserRoleName(userID);   //得到角色名；
    string employeeNo   = Chaokers.CN.SysMembership.API.GetUserRoleName(userID);   //员工编号；

    //保存登录信息；
    Chaokers.CN.SysMembership.API.Passport.SaveAuthInfo(userID, roleName, employeeNo);

    //加载页面权限集；
    Chaokers.CN.SysMembership.API.LoadUserPrivilegesOfPage(userID);
    
    /*****以下代码为必须内容，如果有其它业务要处理，建议放在该段代码块之外.END*************************/


    Response.Redirect("OperationMenu.aspx", true);
    
    ……

五、登录验证及权限验证代码接入
    
    在业务系统页面基类中实现登录验证及权限验证。
    逻辑代码如下：
    
    public class PageBase : System.Web.UI.Page
    {
        /***权限系统必须字段.BEGIN****/

        public bool isAdministrator;    //标记用户角色是否为系统内置超级管理员组(administrators)；
        public bool canAccessThisPage;  //标记是否可以访问该页面；
        public bool isCommonPage;       //标记是否为公共页面（也就是不需要任何验证即可访问的页面）；

        public string userID;           //用户名，例如：zhangsan
        public string roleName;         //角色名，例如：administrators
        public string employeeNo;       //必须，  例如：001

        /***权限系统必须字段.END****/   
        
        
        …………
        
        
        //必须有页面初始化事件，在该事件中执行登录验证及页面访问权限的验证；
        protected override void OnInit(EventArgs e)
        {            
            //检查是否登录，如果登录，则取得用户名、角色名及员工编号；
            if (!Chaokers.CN.SysMembership.API.Passport.Authenticate(ref userID, ref roleName, ref employeeNo))
            {
                Response.Redirect(this.ApplicationPath + "/SystemEntry/SysLogin.aspx");
            }

            //检查是否有访问该页面的权限，如果没有，则终止页面执行，如果有，则取得 isAdministrator、isCommonPage 的值；
            if (!Chaokers.CN.SysMembership.API.Privilege.HasAccessPrivilege(userID, roleName, ref isAdministrator, ref isCommonPage))
            {
                Response.Write("您没有访问该页面的权限！");
                Response.End();
            }

            //程序能运行至此，说明肯定有对该页面的访问权限，通过canAccessThisPage变量来做标记；
            this.canAccessThisPage = true;

            //调用基类的页面初始化事件；
            base.OnInit(e);
        }
        
        …………
        
        //必须有页面预呈现事件，在该事件中应用按钮执行权限；
        public void Page_PreRender(object sender, EventArgs e)
        {
            //应用按钮权限；
            if (this.canAccessThisPage) //如果可以访问当前页面；
            {
                if (!isAdministrator)  //如果不是超级管理员（因为超级管理员可以执行所有按钮）；
                {
                    if (!isCommonPage)  //如果不是公共页面，因为公共页面上的按钮是都可以执行的；
                    {
                        Chaokers.CN.SysMembership.API.Privilege.ApplyBtnExecutePrivilege(userID, roleName, this.Page);
                    }
                }
            }
            else
            {
                //如果不能访问该页面，也就没必要再设置该页面上的按钮的状态了；
            }
        }
        
        …………
    }
    

六、加载业务系统中的树形操作菜单
    
    实现思路：通过树形结构，加载用户（所对应的角色）有权访问的所有模块及节点；

    
    前台代码如下：
    
    ……
    
    <!--外层的Repeater用来显示模块信息，内层的TreeView用来加载模块下的权限节点-->
	<asp:Repeater ID="myRepeater" runat="server">
    <ItemTemplate>
    <table border="1" width="100%">
    <tr>
    <td><%# Eval("ModuleName")%></td>
    </tr>    
    <tr>
    <td>
        <asp:TreeView ID="treeMenu" runat="server" ShowLines="true"></asp:TreeView>
    </td>
    </tr>    
    </table>
    </ItemTemplate>
    </asp:Repeater>
    <asp:Label ID="lblNote" runat="server"></asp:Label>
    
    ……
    
    相应的后台代码如下： 
    
    public partial class OperationMenu : PageBase
    {
        //存储用户所有的节点权限；
        public DataTable userPrivilegeNodes;

        protected void Page_Load(object sender, EventArgs e)
        {            
            if (!this.Page.IsPostBack)
            {
                this.LoadMenu();
            }
        }

        void LoadMenu()
        {            
            DataTable dtb = Chaokers.CN.SysMembership.API.GetUserModules(userID);
            if (dtb.Rows.Count == 0)
            {                
                lblNote.Text = "<div style='padding:5px;border:solid 1px #cccccc;background-color:#f5f5f5;font-size:14px;'>没有可用权限！</font>";
                return;
            }

            
            myRepeater.DataSource = dtb;
            myRepeater.DataBind();

             //获取用户拥有的所有的节点权限；
            userPrivilegeNodes = Chaokers.CN.SysMembership.API.Privilege.GetUserPrivilegesOfNode(userID);

            //每个模块中由一个TreeView控件，用来加载该模块中的节点权限；
            for (int i = 0; i < myRepeater.Items.Count; i++)
            {
                DataRow r = dtb.Rows[i];

                int moduleID = (int)r["ModuleID"];

                TreeView treeView = (TreeView)myRepeater.Items[i].FindControl("treeMenu");
                LoadTreeNodes(treeView, moduleID);
            }
        }        

        /// <summary>
        /// 加载根节点；
        /// </summary>
        private void LoadTreeNodes(TreeView treeView, int moduleID)
        {
            //从用户的所有节点权限集中筛选出数据该模块（模块ID为 moduleID ）的节点权限；
            DataRow[] rows = userPrivilegeNodes.Select("ParentID=0 AND ModuleID=" + moduleID.ToString());
            int rowCnt = rows.Length;

            if (rowCnt > 0)
            {
                for (int i = 0; i < rowCnt; i++)
                {
                    DataRow row = rows[i];

                    TreeNode node = new TreeNode();

                    node.Value  = row["PrivilegeID"].ToString();
                    node.Text   = row["PrivilegeName"].ToString();
                    string target = row["Target"].ToString().ToLower();

                    string defaultPageURL = row["DefaultPageURL"].ToString();
                    if (!String.IsNullOrEmpty(defaultPageURL))
                    {
                        if (target == "default")
                        {
                            //node.NavigateUrl = this.ApplicationName + row["DefaultPageURL"].ToString();
                            node.NavigateUrl = "javascript:WinOpen('" + this.ApplicationName + row["DefaultPageURL"].ToString() + "');";
                        }
                        else if (target == "_blank")
                        {
                            node.NavigateUrl = this.ApplicationName + row["DefaultPageURL"].ToString();
                            node.Target = "_blank";
                        }
                    }
                    else
                    {
                        //node.NavigateUrl = this.ApplicationName + row["DefaultPageURL"].ToString();
                        node.NavigateUrl = "javascript:WinOpen('" + this.ApplicationName + row["DefaultPageURL"].ToString() + "');";
                    }

                    node.Expand();

                    treeView.Nodes.Add(node);

                    AddChildNotes((int)row["ModuleID"], int.Parse(node.Value), node);                    
                }
            }
        }

        /// <summary>
        /// 加载子节点；
        /// </summary>
        /// <param name="parentID"></param>
        /// <param name="parentNode"></param>
        private void AddChildNotes(int moduleID, int parentID, TreeNode parentNode)
        {
            DataRow[] rows = userPrivilegeNodes.Select("ParentID=" + parentID + " AND ModuleID=" + moduleID.ToString());
            int rowCnt = rows.Length;

            if (rowCnt > 0)
            {
                for (int i = 0; i < rowCnt; i++)
                {
                    DataRow row = rows[i];
                    TreeNode node = new TreeNode();

                    node.Value = row["PrivilegeID"].ToString();
                    node.Text = row["PrivilegeName"].ToString();
                    string target = row["Target"].ToString().ToLower();

                    string defaultPageURL = row["DefaultPageURL"].ToString();
                    if (!String.IsNullOrEmpty(defaultPageURL))
                    {
                        if (target == "default")
                        {
                            //node.NavigateUrl = this.ApplicationName + row["DefaultPageURL"].ToString();
                            node.NavigateUrl = "javascript:WinOpen('" + this.ApplicationName + row["DefaultPageURL"].ToString() + "');";
                        }
                        else if (target == "_blank")
                        {
                            node.NavigateUrl = this.ApplicationName + row["DefaultPageURL"].ToString();
                            node.Target = "_blank";
                        }
                    }
                    else
                    {
                        node.NavigateUrl = "#";
                    }

                    node.Expand();

                    parentNode.ChildNodes.Add(node);

                    AddChildNotes((int)row["ModuleID"], int.Parse(node.Value), node);
                }
            }
        }

        public string ApplicationName
        {
            get
            {
                string applicationName = System.Web.HttpContext.Current.Request.ApplicationPath; //如果是站点，则该值为“/”；如果是虚拟目录，则该值可能为："/SysMembershipV1.2"

                //如果部署在虚拟目录下；
                if (applicationName == "/")
                {
                    applicationName = "";
                }

                return applicationName;
            }
        }
    }


七、与业务系统中员工信息绑定    
    为了实现将登录账号与业务系统中员工的绑定，应重写/Controls/SysMembership/User/UserEdit.aspx中的GetEmployees()方法。

八、录入公共页面    
   
    所谓公共页面，即指不需要任何验证即可访问的页面，例如：管理系统的框架页；
    公共页面不作为具体权限项，不需要（也无法）对角色赋予访问公共页面的权限；    
    以系统内置账号administrator（密码默认为：admin）登录系统，录入公共页面；


九、内帐角色及账号    

    权限系统内置了一个超级管理员角色：administrators，同时内置一个拥有该角
    色的超级管理员用户：administrator（密码为：admin），在角色管理和操作员
    管理页面中是看不这些的。
    

十、权限类型说明
    
    权限访问三种类型：
    1）节点权限；
       节点的具体示例为TreeView控件的节点，如果有节点访问权限，则在登录后应可以
       看到该节点，否则看不到。每个节点下可能有多个页面组成，例如：日报表（
       日报表内部就有查询页面、报表页面、图表页面等），在创建节点的时候，应
       指定该节点的入口页面（也就是点击该节点后默认打开的页面）。
    
    2）页面权限；
       拥有页面权限才能访问该页面，您可能在节点中看不到该页面的相关信息，但
       只要有访问该页面的权限，即可在登录后通过任何途径打开它。

    3）按钮权限；
       拥有按钮权限才能执行按钮，否则点击按钮后会提示“您没有执行该按钮的权限”，
       按钮权限隶属于页面访问权限。 
    

十一、其它说明

    对于角色为administrators的用户，权限系统的处理逻辑不对其进行任何的页面
    访问权限和按钮执行权限验证，也就是说拥有所有的操作权限。
    
  