转载: http://www.comprg.com.cn/post_show.asp?id=7873
作者:佚名
一、引言
随着.NET 和J2EE 开发平台的推出,传统的软件开发模式开始向B/S 模式转变,这种转变给系统的安全性提出了更高的要求,B/S 模式下,由于HTTP 协议和浏览器的特殊性,可能会造成信息的泄漏甚至使非法用户修改数据。而权限的设置、分配与管理是任何一个系统必不可少的功能。如何设计与实现其功能,目前有多种方案,传统的访问控制方法DAC (Discretionary Access Control ,自主访问控制模型)、MAC (Mandatory Access Control ,强制访问控制模型)难以满足复杂的企业环境需求。本文采用了现在流行的基于角色访问控制(RBAC ,Role Based Access Control )的基本思想,巧妙地利用了Web 控件TreeView 及ASP.NET 技术,设计了在B/S 模式下,根据不同的用户,不同的角色权限,动态生成用户菜单树的一种具体实现方法。
本文将先介绍RBAC 的基本思想,在此基础上,给出在ASP.NET 中的具体实现方法。
二、角色访问控制(RBAC)的基本思想
图1 RBAC 的基本思想
在RBAC 中,许可Permissions (特权)就是允许对一个或多个客体执行的权力,角色就是许可的集合,如图1 所示。RBAC 的基本思想就是把整个访问过程分为两步:访问权限与角色关联,角色再与用户关联,从而实现了用户与访问权限的逻辑分离。
RBAC 对访问权限的授权由系统管理员统一管理,系统管理员根据单位中不同的岗位定义不同的角色,用户根据其职能和责任被赋予相应的角色。一旦用户成为某角色的成员,则此用户可以完成该角色所具有的职能。
由于RBAC 实现了用户与访问权限的逻辑分离,因此它极大地方便了权限管理的复杂性。可根据用户的实际工作岗位将用户与角色关联,一方面定义角色、增加删除角色中的用户易于操作,另一方面,可以通过更改角色的权限实现大批量用户权限的更新。在实际应用中,由于角色/ 权限之间的变化比用户/ 角色之间的变化要慢得多,当一个用户的职位发生了变化,只要将用户当前的角色去掉,加入代表新职务的角色即可。因此,RBAC 的优点是明显的,更符合企业的应用特征。
三、用户权限菜单树
用TreeView 控件动态实现用户权限菜单树的基本思想是:根据角色访问控制(RBAC )的基本原理,给用户分配不同的角色,每个角色对应一些权限,然后根据用户ID 获取用户对应的角色集合,由角色集合获取对应的权限集合,再由权限集合利用TreeWiew 控件动态生成一棵由该用户对应的角色能访问的页面(模块)组成的权限树。这样,用户无权访问的页面在权限菜单树就不会出现,不同的用户进入的界面不同,实现了用户权限的统一管理。
下面从功能模块设计、数据库设计、架构设计等几方面来阐述其实现过程。
1 .功能模块划分
图2 用户权限管理功能模块图
用户权限管理系统功能如图2 所示,其中主要模块功能阐述如下:
用户管理模块分为:删除用户、浏览用户和角色分配三个子模块,主要负责各类用户的删除、合法性验证及为用户分配角色。用户管理模块中并没有增加用户子模块的功能,主要是由用户注册模块来实现的。
权限管理模块分为:角色管理和访问控制两个子模块。角色管理负责管理各类角色(增、删、改),为角色授予相应信息服务模块的使用权限,删除角色的某个模块的 使用权限等;访问控制是保证信息安全的关键,用户登录时经身份验证后,系统根据用户具有角色的信息服务模块的使用权限自动生成访问权限集,使得用户能够访 问授权的信息,拦截对没有授权信息服务的访问。
2. 数据库设计
在实施RBAC 生成用户权限菜单树时,为了提高系统管理及数据访问的效率,在数据库中设计了用户表(表1 所示)、角色表(表2 所示)、用户角色表(表3 所示)、角色权限表(表4 所示)及菜单树结构表(表5 所示)共5 个表。
表1 用户信息表Users
字段名 |
类型 |
字段中文名 |
UserID |
int |
用户ID 主键 |
UserName |
Varchar(50) |
用户名 |
Password |
Varchar(100) |
用户密码 用MD5 加密 |
RealName |
Varchar(50) |
用户真实姓名 |
|
Varchar(100) |
用户Email |
State |
int |
用户状态,默认:0 |
Baoliu |
Char(1) |
保留,默认:1 |
表2 角色信息表Roles
字段名 |
类型 |
字段中文名 |
RoleID |
int |
角色ID 主键 |
RoleName |
Varchar(20) |
角色名称 用MD5 加密 |
RoleDesc |
Varchar(50) |
角色描述 |
PerMission |
Varchar(50) |
角色权限 |
Baoliu |
Char(1) |
保留,默认:1 |
表3 用户角色表UserRoles
字段名 |
类型 |
字段中文名 |
RoleID |
int |
角色ID |
UserID |
int |
用户ID |
表4 角色权限表RolePermissions
字段名 |
类型 |
字段中文名 |
RoleID |
int |
角色ID |
TreeID |
int |
菜单ID |
ParentID |
Int |
父菜单ID |
表5 菜单树结构表Tree
字段名 |
类型 |
字段中文名 |
TreeID |
int |
菜单ID 主键 |
Title |
Varchar(200) |
菜单标题 |
Desn |
Text(16) |
菜单描述 |
ParentID |
Int(4) |
菜单父ID |
Url |
Varchar(200) |
菜单链接地址 |
Web 程序中,将系统功能模块组织成树型结构,每个模块对应一个菜单,菜单之间的父子关系直接反映了模块之间的父子关系,在数据库中用一张表Tree 来存储这种结构。
5 个表的关系如图3 所示。
图3 5 个表之间的关系图
3. 系统架构设计
B/S 模式下,采用三层结构:表示层、逻辑层及数据层。为了编程方便,把数据层分为实体层和数据访问层,把打开、关闭数据库及调用存储过程的代码全部封将在数据访问类SQLHelper.cs 中。实际应用中采用的是四层结构,系统架构设计如图4 所示。
图4 用户权限管理架构图
注:图4 中的箭头为调用关系。
为了提高程序的执行效率,所有的数据操作都是通过类SQLHelper.cs 调用存储过程来实现的。
实体层的几个主要类及关键方法如下:
(1 )用户类User :
GetUserLogin(string sUserName,string sPassword) :用户登录验证。
GetRoleByUser(int nUserID) :由用户ID 返回该用户所有的角色集。
AddUserRole(int nUserID,int nRoleID):
为用户分配角色。
GetUserPermissionList( int userID ) :得到用户的所有权限集。
(2 )角色类RoleEntity :
AddRoleModule(int nTreeID,int nRoleID) :为角色分配权限。
DeleteRoleModule(int nRoleID) :删除角色拥有的权限。
GetModuleByRole(int nRoleID) :由角色ID 得到该角色的权限集。
(3 )权限树类Tree :
BindTree(TreeView treeView,int userID): 生成树目录。
CreateChildNode(TreeNode parentnode,DataTable dataTable) :递归函数生成树结点。
GetTreesByUserID(int nUserID) :由用户ID 得到用户权限集动态生成用户权限树。
(4 )用户权限检查类CkeckAuthority :
ChkPermission( int userID ) :检查用户是否有访问权限。
IsInRole(int roleID) :检查角色是否存在。
4 .具体实现
在树型结构中,给角色授权时采取简单方式,每次授权时,将其对应的RoleID 、TreeID 及ParentID 存入RolePermissions 表中,见图5 。
图5 角色授权
要实现动态生成用户菜单树,在ASP.NET 开发环境中必须安装微软的IE Web 控件(可到微软的网站免费下载),安装完成后,在开发环境中引用Microsoft.Web.UI.WebControls.dll ,这样在开发环境的工具箱中将会出现MutiPage 、TabStrip 、Toobar 和TreeView 等IE Web 控件。
将IE Web 控件TreeView 添加到Web 页分以下两步:
(1 )在页面顶端添加以下@Register 指令:
<%@Register TagPrefix=”iewc”
Namespace=” Microsoft.Web.UI.WebControls”
Assembly=” Microsoft.Web.UI.WebControls” %>
(2 )在Web 页中需显示TreeView 的位置添加以下Web 控件语法:
<iewc:TreeView runat=”server” …>
…
</ iewc:TreeView>
关于TreeView 控件的用法,读者可参看相应的书籍。
总体架构页面包括用户登录(Longin.aspx )和主界面(Default.aspx )两个部分。
主界面分为三个部分:上部分为标题条(Head.aspx) ,左部分为菜单目录树(LeftTree.aspx ),右部分主框架(MainFram.aspx )。其中LeftTree.aspx 页面含TreeView 控件,用户单击树中的菜单结点就可打开相应的操作页面。
用户登录采用窗体式验证,负责提供用户的身份验证。然后根据用户在系统中所扮演的角色,取出该用户对应的权限集,动态生成该用户对应本系统的操作权限树(见图6 )。
图6 用户访问权限示意图
登录体Login.aspx 中的主要代码如下:
1.private void LoginBtn_Click(object sender, System.EventArgs e) 2. 3.{ 4. 5.if(Page.IsValid == true) 6. 7.{ 8. 9. Entity.User user=new User(); 10. 11. string userId = ""; 12. 13. string isValid=""; //用户的合法标志 14. 15. //用户身份鉴别 16. 17. SqlDataReader recs = user.GetUserLogin(UserName.Text.Trim(), 18. 19.Entity.User.Encrypt(Password.Text.Trim())); 20. 21.if(recs.Read()) 22. 23. { 24. 25. userId = recs["UserID"].ToString(); 26. 27.//取到用户的合法标志 28. 29.isValid=recs["State"].ToString(); 30. 31. } 32. 33. recs.Close(); 34. 35. if(isValid=="0") //用户没有被系统管理同确定,转向访问拒绝面 36. 37.{Response.Redirect("AccessDenied.aspx");} 38. 39.if((userId != null) && (userId != "")) 40. 41.{ Session["UserID"] =userId; 42. 43.Entity.CkeckAuthority chkau= 44. 45.new CkeckAuthority(); 46. 47.//用户访问权限检验 48. 49.if(!chkau.ChkPermission(Int32.Parse 50. 51.(Session["UserID"].ToString()))) 52. 53.{ // 如无权限直接转向访问拒绝面Response.Redirect("AccessDenied.aspx");} 54. 55.else 56. 57.{//用户有访问权限,创建用户名,密码验证的票据 58. 59.FormsAuthenticationTicket ticket = 60. 61. new FormsAuthenticationTicket( 62. 63. 1, // version 版本 64. 65.UserName.Text.Trim(), // 用户cookie名 66. 67.DateTime.Now, // 发布日期 68. 69.DateTime.Now.AddHours(1),//过期日期 70. 71. false, //cookie 持久性(false) 72. 73.Password.Text.Trim() // 用户密码 74. 75.); 76. 77. // 加密票据 78. 79.String cookieStr = FormsAuthentication.Encrypt(ticket); 80. 81.// 将存有用户名、密码信息的字符串存入cookie 82. 83.Response.Cookies["userticket"].Value = cookieStr; 84. 85.Response.Cookies["userticket"].Path = "/"; 86. 87.Response.Cookies["userticket"].Expires = DateTime.Now.AddMinutes(1); 88. 89.//跳转到由用户权限动态生成的主页面Response.Redirect("Default.aspx"); } 90. 91.} 92. 93.else 94. 95.{ UserName.Text = ""; 96. 97. Password.Text = ""; 98. 99. Message.Text = "你输入的用户名称或者密码有误,请重新输入!"; 100. 101.} 102. 103.} 104. 105.} 106. 107. LeftTree.aspx中的主要代码如下: 108. 109.private void Page_Load(object sender, System.EventArgs e) 110. 111. { … 112. 113. if(!Page.IsPostBack) 114. 115. { // 实例化一个树对象 116. 117. Tree tree = new Tree(); 118. 119. //动态创建树型菜单,参数ModuleTview为TreewView控件对象 120. 121.tree.BindTree(ModuleTview,Int32.Parse(Session["UserID"].ToString())); 122. 123. …} 124. 125.} private void LoginBtn_Click(object sender, System.EventArgs e) { if(Page.IsValid == true) { Entity.User user=new User(); string userId = ""; string isValid=""; //用户的合法标志 //用户身份鉴别 SqlDataReader recs = user.GetUserLogin(UserName.Text.Trim(),Entity.User.Encrypt(Password.Text.Trim())); if(recs.Read()) { userId = recs["UserID"].ToString(); //取到用户的合法标志 isValid=recs["State"].ToString(); } recs.Close(); if(isValid=="0") //用户没有被系统管理同确定,转向访问拒绝面 {Response.Redirect("AccessDenied.aspx");} if((userId != null) && (userId != "")) { Session["UserID"] =userId;Entity.CkeckAuthority chkau=new CkeckAuthority();//用户访问权限检验 if(!chkau.ChkPermission(Int32.Parse(Session["UserID"].ToString()))) { // 如无权限直接转向访问拒绝面Response.Redirect("AccessDenied.aspx");} else{//用户有访问权限,创建用户名,密码验证的票据 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, // version 版本 UserName.Text.Trim(), // 用户cookie名 DateTime.Now, // 发布日期 DateTime.Now.AddHours(1), //过期日期 false, //cookie 持久性 (false)Password.Text.Trim() // 用户密码); // 加密票据 String cookieStr = FormsAuthentication.Encrypt(ticket); // 将存有用户名、密码信息的字符串存入cookie Response.Cookies["userticket"].Value = cookieStr; Response.Cookies["userticket"].Path = "/"; Response.Cookies["userticket"].Expires = DateTime.Now.AddMinutes(1); //跳转到由用户权限动态生成的主页面 Response.Redirect("Default.aspx"); }} else{ UserName.Text = ""; Password.Text = ""; Message.Text = "你输入的用户名称或者密码有误,请重新输入!"; } } } LeftTree.aspx中的主要代码如下: private void Page_Load(object sender, System.EventArgs e) { … if(!Page.IsPostBack) { // 实例化一个树对象 Tree tree = new Tree(); //动态创建树型菜单,参数ModuleTview为TreewView控件对象 tree.BindTree(ModuleTview,Int32.Parse(Session["UserID"].ToString())); …} }
此外,为了防止非法用户直接绕过登录验证窗体而在网址中直接输入访问路径进行非法访问,利用ASP.NET 中的用户控件,直接将票据验证、角色验证封装在用户控件CheckUserAuth.ascx 中,在每一个受控的页面中加入用户控件CheckUserAuth.ascx ,若为非法用户直接转向拒绝访问页AccessDenied.aspx ,这样使得系统更加安全可靠。有关Web.config 中基于Forms 验证的配置方法,本文不再赘述。
运行效果:
角色分别为成绩管理员和系统管理员的菜单树如图7 和图8 所示。
图7 角色为成绩管理员的菜单树
图8 角色为系统管理员的菜单树
四、结语
在B/S 模式下,信息系统的安全设计有了新的需求,相对于传统的C/S 应用程序,B/S 应用程序对安全性提出了更高的要求,不但要考虑数据访问的安全性,还要考虑网络的安全性。
本文以RBAC 的思想,采用多层架构,利用TreeView 控件及ASP.NET 技术,设计了一个根据不同的用户不同的角色权限,动态生成用户权限树的方法,该方法高效、安全,用户只能操作有权限的模块,无权限的模块对用户不可见,不同的用户所进入的界面不同,提高了系统的安全性和可靠性。利用本文的方法,可以迅速构建一个安全、高效的B/S 方式管理信息系统。