一种基于角色的用户访问控制方法

来源:互联网 发布:淘宝即将开售的怎么抢 编辑:程序博客网 时间:2024/05/20 06:49

一种基于角色的用户访问控制方法

北仑国际集装箱码头有限公司 李绍红

内容摘要:
    本文介绍了基于角色的用户访问控制RBAC模型的设计思想,提出了一种简化开发过程、方便用户管理权限的方法,并采用Delphi实现其设计。
关键字:
   RBAC,角色,访问控制
一、引言
随着企业对信息系统越来越高的期望,传统的访问控制方法DAC(Discretionary Access Control,自主访问控制模型)、MAC(Mandatory Access Control,强制访问控制模型)已经难以满足复杂的企业环境需求。因此,90年代初美国国家标准化和技术委员会提出了基于角色的访问控制方法,该方法由于实现了用户与访问权限的逻辑分离,更加符合企业的用户、组织、数据和应用特征,而被越来越多的信息系统所使用。
基于角色的访问控制方法(RBAC-Role-Based Access Control)是目前公认的解决大型企业的统一资源访问控制的有效方法。其显著的两大特征是:
1.减小授权管理的复杂性,降低管理开销。
2.灵活地支持企业的安全策略,并对企业的变化有更大的伸缩性。
二、几个概念
Privilege:功能,是用户可执行的某一具体操作,是权限控制中最小的单位,,比如:用户管理中对用户信息的修改、添加、删除、查看。
Role:角色,是一类功能的集合。
User:用户,是权限的拥有者或主体。用户和权限实现分离,通过授权管理进行绑定。
三、设计原理
RBAC(角色访问控制)的基本思想可简单地理解为,把整个访问控制过程分成两步:访问权限与角色相关联,角色再与用户关联,从而实现了用户与访问权限的逻辑分离。
由于RBAC实现了用户与访问权限的逻辑分离,因此它极大的方便了权限管理。例如,如果一个用户的职位发生变化,只要将用户当前的角色去掉,加入代表新职务或新任务的角色即可,角色/权限之间的变化比角色/用户关系之间的变化相对要慢得多,并且委派用户到角色不需要很多技术,可以由行政管理人员来执行,而配置权限到角色的工作比较复杂,需要一定的技术,可以由专门的技术人员来承担,但是不给他们委派用户的权限,这与现实中情况正好一致。
1、人员在系统中总是扮演某种角色的
例如:小张属于办公厅这个部门,办公厅下面有个办公厅人员这个角色,而小张只是扮演了这个角色而已,当小张被调动另外一个部门时候该角色又会被赋予另一个人,而不会因小张的离开而使业务发生变化。
2、业务逻辑希望面对的是系统中的角色,而非扮演角色的具体的人。
例如:以工作流示例,一个公文的下一步流转的对象是组织部部长,他不关心谁扮演这个部长,人员权限模块知道就行了。
四、设计思想
本文中论述的权限控制思想是基于笔者所在公司正在开发的一个大型业务处理系统的设计,该设计简化了标准的RBAC系统,对资源的控制是通过开发人员对每一个功能模块的控制来实现的。该设计中,角色不能被继承,角色把一些功能集合起来,用户可以拥有某一个角色,同时也可以直接将某个功能赋予该用户。权限控制主要体现在界面菜单和工具栏上。不同权限的用户登录系统后将会看到不同的菜单和工具栏,进入某一个功能界面后,可以控制界面上的各个组件状态,有权限则该组件可用。
该设计的一个好处是,开发人员在增加新功能时才增加功能定义,增加功能定义实际上是增加一个窗体的类名到数据库中,程序调用该功能实际上是创建该窗体的一个实例。而拥有权限管理的最终用户可以自由设置界面(菜单项和工具栏的文字显示,顺序,布局等),开发人员仅维护‘功能定义’部分。
一个完整的权限控制流程描述如下:
首先,开发人员进行功能定义,把每一个窗体作为一个功能登记到数据库中,并赋予唯一的一个功能号,也可以直接登记一个功能(如:注销用户,登录系统),权限控制系统根据功能号进行控制;
然后,登记菜单,菜单再跟功能关联,一个功能可以关联多个菜单项;
接着,添加角色,并分配一定的权限,即功能;
最后,登记用户,并赋予一个角色,或直接把某功能赋给该用户。
系统启动时扫描该用户对应角色下的功能和拥有的直接的功能,生成用户功能集,然后扫描整个菜单结构,某项菜单关联了可用的功能,则该菜单被显示出来,否则将忽略。在用户打开窗体时,系统将再次扫描用户权限表,以控制该窗体上的组件状态,从而达到权限控制的目的。
五、表结构设计

   

2.数据表结构设计如下:

六、程序实现
本文中使用Delphi作为开发工具来实现。在程序中,建立一个单元文件UFunctions.pas进行权限控制,在系统登录时调用权限控制模块生成用户界面。此处仅列出主要的实现代码。
1.封装权限控制代码(UFunctions.pas)
//用户类
type
  TUser = class(TObject)
  private
    _UserID: string;//用户代码
    _UserName: string;//用户名
    _PassWord: string;
 public
    constructor Create();
    property UserID: string read _UserID;
property UserName: string read _UserName;
//登录用户
function Login(UserID, PassWord: string): Integer;
//注销用户
procedure Logout;
//修改密码
    function ChangePassWord(OldPassWord, NewPassWord: string): boolean;
  end;

//权限类
type
 TFunctions=class(TObject)
 private
//整个菜单数据,功能号和窗体类的关联,用户可用的功能集
_cdsAllMenu,_cdsFunc,_cdsUserFunc: TClientDataSet;
    _User:TUser;
    //若子菜单有权限显示,必须把相应的父级菜单也显示出来
    procedure ShowParentMenu(var cdsMenu:TClientDataSet;ParentMenuID:Integer);
    //显示子菜单,被ShowMenu调用
  procedure ShowChildMenu(cdsMenu:TClientDataSet;MenuItem:TMenuItem;MenuID:Integer);
    //显示菜单主过程
    procedure ShowMenu(cdsMenu:TClientDataSet;Sender:TObject;mnuMain:TMainMenu);
    //显示工具栏按钮
    procedure ShowToolButton(cdsUserFunc: TClientDataSet;tbMain:TToolBar);
    //菜单点击动作处理程序
    procedure MenuClick(Sender: TObject);
//根据窗体类名获得窗体类
function GetForm(FormName: string): TForm;
//传入窗体类名和显示模式,创建该窗体类的一个实例
function ShowForm(FormName, FormModel: string): TForm;
    function GetUser:TUser;
    procedure SetUser(Value:TUser);
 public
    //登录用户
property LoginUser:TUser read GetUser write SetUser;
    //根据功能号执行相应的操作,若失败返回False
    function ExeFunc(FuncID:String):Boolean;
    //检查窗体的组件是否有权限,有则使之可用,否则禁用
    procedure CheckFormChildFunc(Frm:TForm;FuncID:String);
    //是否具有某权限
    function HasRight(FuncID:String):Boolean;
//根据用户代码生成菜单和工具栏
procedure ShowMenuTool(UserID:String;var mnuMain:TMainMenu;
var tbMain:TToolBar;Sender:TObject);
//登录系统处理过程
    function Login(UserID, PassWord: string): boolean;
 end;
//根据功能号执行相应的操作,若失败返回False
function TFunctions.ExeFunc(FuncID:String):Boolean;
var
  FormName, FormModel: string;
  Frm:TForm;
begin
 result:=False;
  _cdsFunc.Filtered:=False;
  _cdsFunc.Filter:='FuncID='''+FuncID+'''';
  _cdsFunc.Filtered:=True;
 //若该功能号不存在
if _cdsFunc.IsEmpty then
 begin
  _cdsFunc.Filtered:=False;
  exit;
 end;//if
if FuncID='1001' then//用户登录
else //打开窗体
 begin
    FormName:=_cdsFunc.FieldByName('ControlName').AsString;
    FormModel:=_cdsFunc.FieldByName('FormModal').AsString;
    _cdsFunc.Filtered:=False;
    if FormName <> '' then
    begin
      Frm:=ShowForm(FormName, FormModel);
      CheckFormChildFunc(Frm,FuncID);//控制窗体上的组件状态
    end;
 end;
 _cdsFunc.Filtered:=False;
 result:=True;
end;

//显示菜单
procedure TFunctions.ShowMenu(cdsMenu: TClientDataSet;
Sender: TObject;mnuMain:TMainMenu);
var
 newMenu:TMenuItem;
 cdsTmp:TClientDataSet;
 FuncID:Integer;
begin
      ….
       newMenu:=TMenuItem.Create(nil);
       newMenu.Caption:='【'+cdsTmp.FieldByName('MenuText').AsString+'】';
       FuncID:=GetIntDataSet(cdsTmp,'FuncID',0);
       newMenu.Tag:=FuncID;
       mnuMain.Items.Add(newMenu);
       mnuMain.Items[mnuMain.Items.Count-1].Visible:=True;
       if FuncID<>0 then newMenu.OnClick:=MenuClick;
       ShowChildMenu(cdsMenu,newMenu,cdsTmp.FieldByName('MenuID').AsInteger);
……
end;

//传入窗体类名和显示模式,创建该窗体类的一个实例
function TFunctions.ShowForm(FormName, FormModel: string): TForm;
var
  FormClass: TClass;
  fForm: TForm;
begin
  FormClass := GetClass(FormName);
  FormModel := UpperCase(FormModel);
  if FormClass = nil then exit;
  if FormModel = 'M' then
    fForm := nil
  else if FormModel = 'S' then
    fForm := GetForm(FormName)
  else
    exit;
  if fForm = nil then
    Application.CreateForm(TComponentClass(FormClass), fForm);
  fForm.FormStyle:=fsMDIChild;
fForm.WindowState:=wsMaximized;
  fForm.Show;
  result := fForm;
end;
2.主窗体部分,生成权限控制类的一个实例(UFrmMain)
var
MyFunctions: TFunctions;
MyFunctions := TFunctions.Create;
3.登录部分(UFrmLogin)
procedure TfrmLogin.btnLoginClick(Sender: TObject);
var
 iRet,I:Integer;
 sErr:String;
begin
  iRet:=MyFunctions.LoginUser.Login(edtUserID.Text,edtPassWord.Text);
  Case iRet of
   0:
    sErr:='';
   1:
    sErr:='该账户已被禁用,请联系系统管理员!';
   2:
    sErr:='该账户密码已过期,请联系系统管理员!';
   -1:
    sErr:='用户密码有误,请重新输入!';
   end;
//登录失败
  if iRet<>0  then
  begin
    showmessage(sErr);
    exit;
  end
  else//登录成功
  begin
   frmMain.Show;
    MyFunctions.ShowMenuTool(MyFunctions.LoginUser.WorkID,frmMain.meuMain,
frmMain.tbrMain,frmMain);
    close;
  end;
end;
4.定义一个窗体为功能时需要在该窗体中加入以下代码,使该窗体在系统中进行注册:
initialization
  RegisterClass(TfrmTest);
TfrmTest是窗体的类名。
在‘功能定义’中可以这样登记:
功能号:2001
功能名称:测试功能
受控对象名:TfrmTest
窗体模式:S(S表示该窗体类仅能创建一个实例)
七、总结
    本文论述了一种基于RBAC模型的权限管理系统的实现技术方案,该设计既简化了开发人员的开发工作,也使用户在进行权限分配时更加直观灵活,并支持岗位、权限多变的需求。目前,本公司基于该控制系统的大型软件已进入测试阶段。
八、参考文献
1. 段云所(著)(Duan Yunsuo)。信息安全概论。北京:高等教育出版社(Beijing:Publishing House of Higher Education),2003;
2. 曹天杰,张永平.管理信息系统中基于角色的访问控制.计算机应用,2001,第8期:21;
3. 江资斌,向南平.MIS用户权限管理技术.电脑与信息技术,2002,第五期:57。
九、作者简介
李绍红,男,1982年8月出生,2004年计算机本科毕业于武汉理工大学,在宁波北仑国际集装箱码头有限公司工作至今。

原创粉丝点击