河海大学计算机及信息工程学院 张富国
摘要:本文介绍了一个通用的基于菜单项级别的权限控制方法,分析了它所要解决的主要技术问题,并给出解决这些技术问题的具体步骤。
关键词: 权限管理 Delphi TmainMenu 菜单下载 授权 菜单加载
权限管理一直是应用程序所普遍关心的事,如果能实现应用程序菜单项级别的用户权限控制,那肯定是大家所向往的事。
1. 原理
我们知道菜单项的visible属性可以用来控制菜单项的显示,如果数据库中的某个字段能唯一地表示具体的菜单项的位置,而同一表中的另一字段用来表示用户代码,那么我们就可以通过在加载菜单时检查数据库中是否存在用户代码和某一菜单项的记录来控制该菜单项的显示,从而达到权限管理。研究TMainMenu的属性可以发现,如果说明Menu1:TmainMenu,则Menu1.items[I].caption为菜单各列的名称,而Menu1.Items[I].Items[ii].caption为I列ii行下拉菜单项的名称。所以可以把菜单看作矩阵,用I和ii的行列组合来唯一表示菜单项。一般应用程序中有很多菜单,所以数据库中表示菜单项的字段还应加上应用程序的代码。为此,建表Permission(SQL SERVER为例)通过编制相应的授权程序来记录某用户对某一菜单项是否具有使用权。
表名 Permission
字段名 | 类型 | 说明 |
IndexCode | Char(14) | 索引号 |
OprCode | Char(4) | 用户工号 |
AppCode | Char(4) | 应用程序代码 |
ModCode | Char(6) | 菜单项代码(两位应用程序代码+两位列号+两位行号) |
表名 Application
字段名 | 类型 | 说明 | |
AppCode | Char(4) | 应用程序代码 | |
AppName | Varchar(20) | 应用程序名称 |
表名 Module
字段名 | 类型 | 说明 |
ModCode | Char(6) | 菜单项代码(两位应用程序代码+两位列号+两位行号) |
ModName | Varchar(20) | 菜单项名称 |
PrgCode | Char(2) | 菜单所代表的应用程序代码 |
MenuCol | INT | 菜单项所处的列 |
MenuRow | INT | 菜单项所处的行 |
2.具体实现
2.1菜单下载程序的实现
菜单下载程序须由主菜单Form上某一控件(如为Button1)激活。
// 获得主菜单矩阵,自动下载程序菜单
procedure TMainMenuForm.Button1Click(Sender: TObject);
var
mn_col,mn_row : integer ; //列号和行号
i,ii : integer ; //循环变量
ss : string ; //
str_menu_col,str_menu_row : string;
begin
mn_col := MainMenuForm.MainMenu.Items.Count; //获得菜单列数
//把菜单列记录到Module表中
for i := 1 to mn_col do
begin
ss := MainMenuForm.MainMenu.Items[i-1].Caption; //获得列名称
if i < 10 then
str_menu_col := '0' + inttostr(i)
else
str_menu_col := inttostr(i); //两位列号
str_menu_row := '00'; //行号为零
Close;
SQL.Clear ;
SQL.Add (' Insert into HosServer..Module ') ;
SQL.Add (' (ModCode , ModName, PrgCode, MenuCol, MenuRow)') ;
SQL.Add (' values (:mod_code , :mod_name, :prg_code,:menu_col,:menu_row) ') ;
ParamByName('mod_code').AsString := PrgCode + str_menu_col + str_menu_row ; //PrgCode为菜单所在应用程序代码,已在外部定义值
ParamByName('mod_name').AsString := ss;
ParamByName('prg_code').AsString := PrgCode;
ParamByName('menu_col').AsString := str_menu_col;
ParamByName('menu_row').AsString := str_menu_row;
try
ExecSQL ;
except
Application.MessageBox ('添加Program数据时出现错误!','错误信息',mb_ok+mb_iconinformation);
exit;
end;
mn_row := MainMenuForm.MainMenu.Items[i-1].Count;
for ii := 1 to mn_row do
begin
ss := MainMenuForm.MainMenu.Items[i-1].Items[ii-1].Caption;
ss := trim(ss);
if i < 10 then
str_menu_col := '0' + inttostr(i)
else
str_menu_col := inttostr(i);
if ii < 10 then
str_menu_row := '0' + inttostr(ii)
else
str_menu_row := inttostr(ii);
// Insert Module
Close;
SQL.Clear ;
SQL.Add (' Insert into HosServer..Module ') ;
SQL.Add (' (ModCode , ModName, PrgCode, MenuCol, MenuRow) ') ;
SQL.Add (' values (:mod_code,:mod_name, :prg_code,:menu_col,:menu_row) ') ;
ParamByName('mod_code').AsString:=PrgCode + str_menu_col + str_menu_row ;
ParamByName('mod_name').AsString := ss;
ParamByName('prg_code').AsString := PrgCode;
ParamByName('menu_col').AsString := str_menu_col;
ParamByName('menu_row').AsString := str_menu_row;
try
ExecSQL ;
except
Application.MessageBox ('添加Program数据时出现错误!','错误信息',mb_ok+mb_iconinformation);
exit;
end;
end; //ii
end; //i
end; //with
MessageBox(handle,'操作成功!','提示',MB_OK+MB_ICONINFORMATION);
End;
2.2授权程序的实现
授权程序的最好实现方式是用内含三层节点的TtreeView控件来实现,第一层显示应用
程序名称,第二层显示该应用程序主菜单的列名,第三层显示该应用程序主菜单每列中的菜单项名。当某一菜单项节点的图像指向为时(假设imageIndex=0),表示该用户对该菜单项没有操作权限;反之,当某一菜单项节点的图像为时(假设imageIndex=1),表示该用户对该菜单项有操作权限。因篇幅所限,对这一块的程序只给出以上思路及其中的一个界面
下面介绍在选择完用户名以及该用户相应的权限后,保存权限的程序(假设由Button2的Click事件触发)。
//CurOpr已指定操作员的工号
//TreeView1是菜单的节点树
//Query1为TQuery控件
type
PNodeData = ^TNodeData;
TNodeData = record
AppCode :string[4]; //应用程序代码
ModCode :string[6]; //菜单项代码
end;
procedure TPermissionForm.Button2Click(Sender: TObject);
var
CurNode :TTreeNode; //菜单项节点
pData :PNodeData;
ii:integer ;
begin
with treeview1 do
begin
//先删除该操作员原先设定的权限
Query1.Close;
Query1.Sql.Clear;
Query1.Sql.Add('Delete From HosServer..Permission Where OprCode = :OprCode ');
Query1.ParamByName('OprCode').AsString := CurOpr;
Query1.ExecSql;
Query1.Close;
CurNode := Items.GetFirstNode.GetFirstChild; //Application
while CurNode <> nil do
begin
if (CurNode.Data <> nil) and (CurNode.ImageIndex=1) then //看是否选中
begin
//往表中插入该操作员对该菜单项的操作权限。
pData := CurNode.Data;
Query1.Close;
Query1.Sql.Clear;
Query1.Sql.Add('Insert into HosServer..Permission');
Query1.Sql.Add('(IndexCode,OprCode,AppCode,ModCode )');
Query1.Sql.Add('Values (:IndexCode,:OprCode,:AppCode,:ModCode)');
Query1.ParamByName('IndexCode').AsString:= CurOpr+pData.AppCode+pData.ModCode;
Query1.ParamByName('OprCode').AsString := CurOpr;
Query1.ParamByName('AppCode').AsString := pData.AppCode;
Query1.ParamByName('ModCode').AsString := pData.ModCode;
Query1.ExecSql;
Query1.Close;
end;
CurNode := CurNode.GetNext
end;
end;
MessageBox(handle,'操作成功!','提示',MB_OK+MB_ICONINFORMATION);
end;
有了前面的几步后,最后只要在菜单加载的时候,根据表Permission表中有没有对应项就能决定每一菜单项的显示与否,这样我们就实现了对应用程序菜单项级别的权限控制。
参考文献
1.刘韬,肖永顺,王宇 .Delphi4.0数据库编程. 北京:人民邮电出版社,1999
2.李力,李微,董恒 . Delphi4.0 实用编程指南 重庆:四川大学出版社,1998
Email: c6500410@public1.ptt.js.cn
通讯地址:南京市河海大学610信箱
邮编: 210000
电话:(025)6500410