浪 潮 集 团 计 算 机 事 业 本部
邢 剑 群
---- 数 据 库 的 安 全性 包
括 对 数 据 访 问 的 限 制。 未 经 授 权 的 用 户 不 能 访 问数 据 库, 还 有 些 用 户 不 能 访 问 该 数 据 库 的 某 些 部
分。在 应 用 系 统 中, 系 统 管 理 员 可 以 使 用“ 用 户 管 理” 模块, 为 应 用 系 统 建 立 多 个 用 户, 对 各 个 用 户
的“ 用 户号”、“ 用 户 名”、“ 口 令 字”、“ 网 络 地 址” 等 信 息 进行 维 护, 并 可 根 据 需 要 删 除 某 些 用 户; 系 统
管 理 员 使用“ 权 限 管 理” 模 块, 可 设 置 各 个 用 户 的 操 作 权 限。在 一 个 用 户 可 以 访 问 数 据 库 的 信 息 之
前, 该 用 户 必 须在 登 录 窗 口 中 表 明 自 己 是 一 个 合 法 用 户, 并 提 供 正 确口 令。 登 录 成 功 后, 用 户 可 以
运 行 已 经 授 权 的 模 块 功能, 访 问 相 应 的 数 据 库 信 息, 但 是 不 能 访 问 未 授 权 模块 所 对 应 的 数 据 库 信
息。 这 样, 就 为 应 用 程 序 提 供 了一 级 安 全 性。 当 然, 在 一 个 实 际 的 系 统 中, 可 以 借 助网 络 安 全 性 和 数
据 安 全 性 来 增 强 这 种 安 全 性。 权 限 管理 的 实 现 有 多 种 方 法, 例 如: 可 以 对 各 个 模 块 功 能 所需 权 限 级
别 进 行 定 义, 再 定 义 各 个 用 户 的 权 限 级 别(访 问 级 别), 在 每 个 选 单 项( 菜 单 项) 调 用 相 应 程 序前 先
判 断 当 前 用 户 级 别 是 否 大 于 或 等 于 该 选 单 项 运行 所 需 选 单 权 限 级 别, 若“ 大 于 或 等 于”, 则 运 行
相应 程 序, 否 则, 提 示“ 用 户 无 权 运 行 该 模 块 功 能”。 本文 将 介 绍 一 种 更 为 灵 活 的 方 法, 即 系 统 管 理
员 为 普 通用 户 设 置 可 选 菜 单 项, 当 用 户 进 入 应 用 系 统 时, 只 有其“ 可 选 菜 单 项” 显 示 出 来, 这 样 就 限
定 了 普 通 用 户对 应 用 系 统 各 个 模 块 的 使 用 权 限。 具 体 步 骤 如 下:
---- 1. 表 定 义:
---- 表 名:user_item
---- 作 用: 存 放 用户 信
息。 主 键 列 是“ 用 户 号” 列。
列代码列名 数据类型 空
———— ———— ————
usr_id 用户号 char(8) NOT NULL
usr_name 用户名 char(20) YES
usr_password 口令字 char(6) YES
---- 表 名:menu_item
---- 作 用: 存 放 系统 菜
单 信 息。 主 键 列 是“ 菜 单 项 编 号” 列。
列代码 列名数据类型 空
———— ———————— ——
menu_id 菜单项编号 char(5) NOT NULL
menu_text 菜单项文本 char(40) YES
---- 表 名:menu_right
---- 作 用: 存 放 普通 用
户 可 使 用 菜 单 项 信 息。 主 键 列:“ 用 户 号” 和“ 用户 可 选 菜 单 项 编 号” 列。
列代码列名 数据类型 空
———— ———————— ——
usr_id 用户号 char(8) NOT NULL
menu_id 用户可选菜单项编号char(5) NOT NULL
---- 2. 表 与 表 之 间的
关 系:
---- user_item 表
和menu_right 表 的 关 系 是 一 对 多 的 关 系。menu_right 表 的“ 用 户 号” 列 是user_item 表 的 外 部 键。
如 果 用ERwin 创 建 表, 此“ 父- 子” 关 系 参 照完 整 性 定 义 为:
Trigger Action Type Trigger RI Option
CHID DELETE-----
CHID INSERT RESTRICT
CHID UPDATE RESTRICT
PARENT DELETE CASCADE
PARENT INSERT -----
PARENT UPDATE CASCADE
---- 3. 创 建 索 引:
---- 以usr_id 列 为 索引
列 为user_item 表 建 立Unique Ascending 索 引。
---- 如 果 连 接 的 数据 库
是SYBASE 、INFORMIX 或 SQLServer, 以menu_id 列 为 索 引 列 为menu_item 表 建 立Unique Cluster
Ascending 索 引,sql 语 句 为:
---- create UNIQUE
CLUSTER INDEX XPKmenu_item on menu_item (menu_id ASC);。 以usr_id 列 和menu_id 列 为 索
引列 为menu_right 表 建 立Unique Cluster Ascending 索 引。 如 果 连 接 的 数据 库 是ORACLE
、WATCOMSQL(PowerBuilder5.0 中 改 名 为Sybase SQL Anywhere), 则 无 法 建 立Cluster 索 引。
---- 4. 对 象 的 属性:
---- PowerBuilder 是
面 向对 象 的 开 发 工 具, 采 用 事 件 驱 动 工 作 方 式, 利 用 对 象的 属 性 来 描 述 各 对 象 的 表 现 形 式 和 所 处
的 状 态。 在 应用 程 序 中, 可 以 根 据 需 求 的 不 同, 利 用 属 性 设 置 对 对象 作 不 同 的 处 理。 对 象 属 性 利 用
好 后, 可 以 很 好、 很简 捷 甚 至 无 需 编 程 就 能 满 足 一 些 特 殊 要 求。
---- 对 象 属 性 的 语法
为:Objectname.attribute
---- 本 文 所 述 程 序中 用
到 的 对 象 属 性:
窗口对象的属性名称 属性值类型 属性描述
MenuID Menu型对象变量 指定菜单的标识号(ID)
菜单的属性名称 属性值类型 属性描述
Item[] Menu型数组 指定菜单项
Text 字符串 指定菜单对象的文本内容
---- 5. 用 户 自 定 义函
数:
---- 函 数
名:f_get_menu
---- 作 用: 当 系 统管 理
员 运 行 应 用 系 统 时, 调 用 该 函 数, 可 把 系 统 菜 单信 息 存 放 到 表menu_item 中。
---- 函 数 定 义:
---- Access: Public
Returns: None
---- Arguments
Name: menu Type: menu Pass By: value 注 释: 系 统 主 窗 口 连 接 的 菜 单 标 识。
---- 函 数 内 容:
DELETE FROM menu_item ;
int i,j,k,upper1,upper2,upper3
string id,text
//得到菜单数组的最高界,即一级菜单项个数
upper1=upperbound(menu.item)
for i=1 to upper1
//给一级菜单项编号
id=string(i)+"0000"
//一级菜单项文本内容
text=menu.item[i].text
//把一级菜单项信息存入表menu_item中
INSERT INTO menu_item
(menu_id,
menu_text)
values (:id,:text);
//upper2是第i个一级菜单项的下拉菜单项数
upper2=upperbound(menu.item[i].item)
for j=1 to upper2
//给二级菜单项编号
id=string(i)+string(j)+"000"
//二级菜单项文本内容
text=menu.item[i].text+"."+menu.item[i].item[j].text
//把二级菜单项信息存入表menu_item中
INSERT INTO menu_item
(menu_id,
menu_text)
VALUES(:id,
:text);
//upper3为第i个一级菜单项的第j个
二级菜单项的下拉菜单项数
upper3=upperbound(menu.item[i].item[j].item)
for k=1 to upper3
//给三级菜单项编号
id=string(i)+string(j)+string(k)+"00"
//三级菜单项文本内容
text=menu.item[i].text+"."+menu.item[i].item[j].text+"."&
+menu.item[i].item[j].item[k].text
//把三级菜单项信息存入表menu_item中
INSERT INTO menu_item
(menu_id,
menu_text)
VALUES(:id,
:text);
next
next
next
---- 注: 每 级 菜 单项 编
号 为1 至9, 即 每 级 菜 单 项 数 不 超 过9, 否 则 需 要 修改 此 函 数 内 容。
---- 对 超 过 三 级 的多 级
菜 单 需 要 修 改 此 函 数 内 容。
---- 函 数
名:f_test_menu
---- 作 用: 当 普 通用 户
运 行 应 用 系 统 时, 调 用 该 函 数, 可 把 该 用 户 不 可选 菜 单 项 设 为 不 可 见。
---- 函 数 定 义:
Access: Public Returns: None
Arguments:
Name: Type: Pass By: 注释:
1 menu menu value 菜单标识
2 uidstring value 用户号
函数内容:
string menu_id
//定义光标,查询用户不可选菜单项
DECLARE cur_noright CURSOR FOR
SELECT menu_item.menu_id
FROM menu_item
WHERE menu_item.menu_id not in
( SELECT menu_right.menu_id
FROM menu_right
WHERE menu_right.usr_id = :uid )
order by menu_id;
open cur_noright;
fetch cur_noright into :menu_id;
do while sqlca.sqlcode=0
//如果为一级菜单项则一级菜单项不可见,
一级菜单工具栏选项不可见
if mid(menu_id,2,4 )="0000" then
menu.item[integer(mid(menu_id,1,1))].visible=false
menu.item[integer(mid(menu_id,1,1))].toolbaritemvisible=false
else
//如果为二级菜单项
if mid(menu_id,3,3)="000" then
menu.item[integer(mid(menu_id,1,1))].item[integer(mid(&
menu_id,2,1))].visible=false
//二级菜单项不可见
menu.item[integer(mid(menu_id,1,1))
].item[integer(mid(menu_id,2,1&
))].toolbaritemvisible=false
//二级菜单工具栏选项不可见
else //如果为三级菜单项
menu.item[integer(mid(menu_id,1,1))
].item[integer(mid(menu_id,2,1))].&
item[integer(mid(menu_id,3,1))].visible=false
//三级菜单项不可见
menu.item[integer(mid(menu_id,1,1))
].item[integer(mid(menu_id,2,1))].&
item[integer(mid(menu_id,3,1))].toolbaritemvisible=false
//三级菜单工具栏选项不可见
end if
end if
fetch cur_noright into :menu_id;
//取记录,光标下移一条
loop
close cur_noright;
---- 6. 创 建 授 权
窗口win_asyspermit:
----
在win_asyspermit(Title 为“ 用 户 权 限 管 理”) 上 并 列 放 置 三 个 数 据 窗 口, 数 据窗 口 的Properties
中 选 择Vsible、Enabled、Hscroll Bar、Vscroll Bar、Live Scrolling:
---- dw_1 为dw_user,
显 示管 理 员 已 经 创 建 的 所 有 普 通 用 户 名,dw_user 的 语 法 为:
SELECT "user_item"."usr_id", "user_item"."usr_name"
FROM "user_item"
WHERE trim(user_item.usr_id) <> 'super'
---- 注:super 为 系 统管
理 员 的 用 户 号。dw_user 只 显 示usr_name 列 即 可。
---- dw_2
为dw_unright, 显示 用 户 不 可 选 菜 单 项,dw_unright 的 检 索 参 数 为 字 符 型uid, 代 表 用 户
号,dw_unright 的 语 法 为:
SELECT "menu_item"."menu_text", "menu_item"."menu_id"
FROM "menu_item"
WHERE menu_item.menu_id not in
( SELECT "menu_right"."menu_id"
FROM "menu_right"
WHERE menu_right.usr_id = :uid )
ORDER BY "menu_item"."menu_id" ASC
注:如果已对表menu_item创建了聚簇索引,
则可略去ORDER BY "menu_item"."menu_id" ASC
dw_unright只显示menu_text列即可。
dw_3为dw_user_right,显示用户可选菜单项,
dw_user_right的检索参数为字符型uid,代表用户号,
dw_user_right的语法为:
SELECT "menu_right"."usr_id",
"menu_right"."menu_id",
"menu_item"."menu_text"
FROM "menu_right",
"menu_item"
WHERE ( "menu_right"."menu_id" =
"menu_item"."menu_id" ) and
( ( menu_right.usr_id = :uid ) )
ORDER BY "menu_right"."menu_id" ASC
注:如果已对表menu_item创建了聚簇索引,
则可略去ORDER BY "menu_item"."menu_id" ASC
dw_user_right只显示menu_item_menu_text列即可。
窗口win_asyspermit上定义实例变量string uid,
代表用户号。在窗口open事件中输入下列script:
connect;
settransobject(dw_1,sqlca)
settransobject(dw_2,sqlca)
settransobject(dw_3,sqlca)
dw_1.retrieve() //dw_1中显示所有普通用户名
long rows
rows=dw_1.rowcount()
if rows >0 then
uid=dw_1.getitemstring(1,"usr_id")
dw_1.scrolltorow(1)
dw_1.selectrow(1,true)
dw_1.setfocus()
dw_2.retrieve(uid)
//dw_2显示出第一个用户的不可选菜单项
dw_3.retrieve(uid)
//dw_3显示出第一个用户的可选菜单项
end if
dw_1的clicked事件中输入下列script:
if row >0 then
uid=dw_1.getitemstring(row,"usr_id")
dw_2.retrieve(uid)
dw_3.retrieve(uid)
end if
dw_1的rowfocuschanged事件中输入下列script:
long rows,r
rows=getrow()
r=dw_1.rowcount()
do while r >0
dw_1.selectrow(r,false)
r=r -1
loop
dw_1.selectrow(rows,true) //被选中的行反显
dw_2的doubleclicked事件中输入下列script:
long rows
string menu_id,m_id,userid
rows=dw_2.getrow()
menu_id=dw_2.getitemstring(rows,"menu_id")
if mid(menu_id,2,4)="0000" then
//如果当前双击的是一级菜单项
insert into menu_right(usr_id,
menu_id)
values(:uid,
:menu_id);
//把当前一级菜单项使用权赋给用户uid
else
if mid(menu_id,3,3)="000" then
//如果当前双击的是二级菜单项
insert into menu_right(usr_id,
menu_id)
values(:uid,
:menu_id);
//把当前二级菜单项的使用权赋给用户uid
m_id=mid(menu_id,1,1)+"0000"
//当前二级菜单项所属的一级菜单项编号
//定义光标c1
declare c1 cursor for
select usr_id from menu_right
where usr_id=:uid and menu_id=:m_id;//光标对应的select语句
open c1; //打开光标,执行对应的SQL SELECT语句
fetch c1 into :userid; //判断uid是否具有当前
二级菜单项所属一级菜单项的使用权
if sqlca.sqlcode=100 then //如果uid不具有当前
二级菜单项所属一级菜单项的使用权
insert into menu_right(usr_id,
menu_id)
values(:uid,
:m_id);//把当前二级菜单项所属的一级
菜单项的使用权赋给用户uid
end if
close c1;
else //如果当前双击的是三级菜单项
insert into menu_right (usr_id,
menu_id)
values(:uid,
:menu_id);//把当前三级菜单项的使用权赋给用户uid
m_id=mid(menu_id,1,2)+"000"//当前
三级菜单项所属的二级菜单项的编号
//定义光标c2
declare c2 cursor for
select usr_id from menu_right
where usr_id=:uid and menu_id=:m_id;//光标对应的SELECT语句
open c2; //打开光标,执行对应的SQL SELECT语句
fetch c2 into :userid; //判断uid是否具有当前
三级菜单项所属的二级菜单项的使用权
if sqlca.sqlcode=100 then //如果uid不具有当前
三级菜单项所属的二级菜单项的使用权
insert into menu_right(usr_id,
menu_id)
values(:uid,
:m_id); //把当前三级菜单项所属的
二级菜单项的使用权赋给用户uid
end if
close c2;
m_id=mid(menu_id,1,1)+"0000" //当前三级菜单项所属
二级菜单项所属的一级菜单项的编号
//定义光标c3
declare c3 cursor for
select usr_id from menu_right
where usr_id=:uid and menu_id=:m_id; //光标对应的SELECT语句
open c3; //打开光标,执行对应的SQL SELECT语句
fetch c3 into :userid; //判断uid是否具有当前三
级菜单项所属二级菜单项所属的一级菜单项的使用权
if sqlca.sqlcode=100 then //如果uid不具有当前
三级菜单项所属二级菜单项所属的一级菜单项的使用权
insert into menu_right(usr_id,
menu_id)
values(:uid,
:m_id); //把当前三级菜单项所属二
级菜单项所属的一级菜单项的使用权赋给用户uid
end if
end if
dw_3.retrieve(uid)
dw_2.retrieve(uid)
dw_3的doubleclicked事件中输入下列script:
int rows
string menu_id,m_sub
rows=dw_3.getrow()
menu_id=dw_3.getitemstring(rows,"menu_id")
if mid(menu_id,2,4)="0000" then //如果双击的是一级菜单项
m_sub=mid(menu_id,1,1)
delete from menu_right
where substring(menu_id,1,1)=:m_sub and
trim(menu_right.usr_id)=trim(:uid);//
删除当前一级菜单项及其所有的下拉菜单项
else
if mid(menu_id,3,3)="000" then
//如果双击的是二级菜单项
m_sub=mid(menu_id,1,2)
delete from menu_right
where substring(menu_id,1,2)=:m_sub and
trim(menu_right.usr_id)=trim(:uid);
//删除当前二级菜单项及其所有的下拉菜单项
else
//如果双击的是三级菜单项
DELETE FROM menu_right
WHERE ( menu_right.usr_id = :uid ) AND
( trim(menu_right.menu_id) = trim(:menu_id) );
//删除当前三级菜单项
end if
end if
dw_2.retrieve(uid)
dw_3.retrieve(uid)
---- 7. 创 建MDI 类 型的
系 统 主 窗 口win_asysmain_mdi:
---- 连 接MDI 风 格 的系
统 主 菜 单, 在win_asysmain_mdi 的open 事 件 中, 输 入 下 列script:
---- long a
---- a=backcolor
---- if
trim(yhh)='super' then //yhh 为 全 局 变 量, 是 用 户 登 录 时 录 入 的 用 户
号f_get_menu(win_asysmain_mdi.menuid)// 如 果 是 系 统 管 理 员 进 入 系 统, 则 调 用 自 定 义 函
数f_get_menu 把 主 菜 单 所 有 信 息 存 入menu_item 表 中else // 如 果 是 普 通 用户 进 入 系
统f_test_menu(win_asysmain_mdi.menuid,yhh)// 调 用 自 定 义 函 数f_test_menu 把 用 户 不 可 选
菜 单 项visible 属 性 设 为false
---- end if
---- 8. 创 建 用 户 登录
窗 口( 略):
---- 用 户 登 录 成 功后,
打 开 系 统 主 窗 口win_asysmain_mdi。
---- 9. 上 述 程 序 使用
说 明:
---- 用 户 登 录 时,系 统
判 断 如 果 用 户 号 合 法, 输 入 口 令 正 确, 则 进 入 系统 主 窗 口, 如 果 当 前 用 户 号 为“super”( 系 统 管 理
员), 则把 主 菜 单 所 有 信 息 存 入menu_item 表 中, 系 统 管 理 员 运 行主 菜 单 上 “ 权 限 管 理” 选 项, 进 入
权 限 管 理 窗 口,在“ 用 户 管 理” 模 块 中 设 定 的 所 有 用 户 名 显 示 在 授 权窗 口win_asyspermit 的 数 据 窗
win_asyspermit. dw_1 中, 用 鼠 标 双 击 某用 户 名, 则 此 用 户 不 可 选 菜 单 项 显 示 在
win_asyspermit. dw_2 中 , 此 用 户 可 选 菜 单 项 显 示 在win_asyspermit. dw_3 中, 如 果 是 第一 次
为 此 用 户 设 定 权 限, 则“ 不 可 选 菜 单 项” 中 显 示系 统 主 菜 单 所 有 项。 在“ 不 可 选 菜 单
项”(win_asyspermit. dw_2) 中 双 击 某 菜 单 项, 则 此 菜 单 项 从“ 不 可 选 菜 单项”(win_asyspermit.
dw_2) 中 消 失, 并 添 加 到“ 可 选 菜 单项”(win_asyspermit. dw_3) 中。 同 样, 在“ 可 选 菜 单
项”(win_asyspermit. dw_3) 中 双 击 某 菜 单 项, 则 此 菜 单 项 从“ 可 选 菜 单项”(win_asyspermit.
dw_3) 中 消 失, 并 添 加 到“ 不 可 选 菜 单项”(win_asyspermit. dw_2) 中。 这 样, 管 理 员 便 可 为 各 个
普 通用 户 赋 权 限。
---- 注 意: 如 果 把某 下
级 菜 单 项 赋 给 用 户, 则 其 上 级 菜 单 项 被 系 统 自 动赋 给 用 户; 如 果 把 某 用 户 可 选 菜 单 项 取 消, 则 系
统 自动 取 消 用 户 对 此 菜 单 项 的 所 有 下 级 菜 单 项 的 使 用 权。系 统 管 理 员 应 把“ 用 户 管 理”、“ 权 限 管
理” 等 系 统 管理 子 模 块 设 为 普 通 用 户“ 不 可 选 菜 单 项”。
---- 如 果 当 前 用 户号 不
是“super”, 而 是 普 通 用 户, 则 只 显 示 该 用 户 可 选 菜单 项, 该 用 户 只 能 运 行 相 应 模 块。
---- 10. 附 注:
---- 创 建 授 权 窗
口win_asyspermit 时, 可 以 用lb_1、lb_2、lb_3 取 代dw_1、dw_2、dw_3, 在script 语 句 中可 定 义 光
标, 把 表 中 某 列 内 容 逐 条 取 出, 用 函 数listboxname.AddItem(item) 增 加 到 列 表 框 中, 还 可 用 函
数 listboxname.Reset() 清 除 列 表 框中 所 有 项。 详 细script 在 此 不 再 赘 述。
11. 上 述 程 序 具 有 很 强 的 实 用 性,
在PowerBuilder5.0 环 境 下 调 试通 过。
|