一个权限管理的方案,大家来讨论一下优缺点 ,顺便给个成功的案例。(100分)

  • 主题发起人 eastphoenix
  • 开始时间
E

eastphoenix

Unregistered / Unconfirmed
GUEST, unregistred user!
小弟刚刚接触实际应用,不知应用程序中的权限管理该如何做,今天下午偶然想到了一个方
案,请各位高手点评一下优缺点,顺便提供几个权限管理的成功方法。谢谢。

建立一个权限表,包括权限等级,函数标志。用户在登录时用自己的用户名和密码登录之后
,在用户表里记录自己的权限等级,以后每次调用函数都检查用户的权限等级和这个函数的
关系。例:
用户表
用户名 密码 权限等级
Liu 123 1
Zhang 234 2
权限和相应函数对照表
权限等级 函数标志
1 Aaa
2 bbb
1 bbb
用户每次调用函数提供自己的用户名(session)函数可能如下:
procedure Aaa(const session, pwd, out isSuccess)
var
procedureID:String;
begin
isSuccess := false;
procedureID := ‘Aaa’;//函数标志,不是函数名,也可以和函数重名。
Select * from权限和相应函数对照表,用户表 where用户名=session and
密码 = pwd and 用户表.权限等级=权限和相应函数对照表. 权限等级
and函数标志=procedureID;
If 记录数=0 then
exit;//如果此用户名所拥有的权限不拥有此函数的使用权
//则退出。
……//否则执行函数。
……
isSuccess := true;
end;

修改权限等级可执行的函数时,只需在表中插入或删除记录即可
为了不让用户了解更多的组件内部函数,可以自己编写一个程序来进行权限表的修改。
优点:权限管理清晰方便。可以在代码级设定权限。
缺点:每个函数调用都要查询数据库,不知是否服务器受得了。

一个改进方法:

组件模块建立一个全局数组,存放每一个函数调用者的权限等级,在初始化时从数据库中提
取并赋值。用户提交自己的权限等级,然后在函数中判断
用户每次调用函数提供自己的权限等级(level)函数可能如下:
procedure Aaa(const level, out isSuccess)
begin
isSuccess := false;
if level <> gloabRight.getRight(Aaa)
//gloabRight.getRight()为一个类成员方法,用于返回函数标志为Aaa的权限等级。
Exit
else
……//执行函数
……
isSuccess := true;
end;
end;

优点:只在模块初始化时读一次数据库,减少了系统额外负担。
缺点:当一个函数标志对应多个权限等级时,需要编码做额外处理,暂时没仔细想怎么做。
无法对用户提供的level做判断,只能假定是合法的,和直接提供session而不提供密码是
一样的。

另一种改进方法:
权限表
权限等级 权限执行码(暂定)
1 111
2 110
3 100
其中权限执行码根据函数表和权限和相应函数对照表生成
函数表
函数名 二进制编码
Aaa 001
Bbb 010
Ccc 100
权限和相应函数对照表
权限等级 函数标志
1 Aaa
1 Bbb
1 Ccc
2 Bbb
2 Ccc
3 Ccc
用户登录后,从数据库中找出他所拥有权限的执行码发给用户,每次用户调用的时候传递一
个执行码,在函数中进行判断
用户每次调用函数提供自己的权限执行码(exeCode)函数可能如下:
procedure Aaa(const exeCode, out isSuccess)
begin
isSuccess := false;
if (exeCode 逻辑与 001) = 0 then
exit
else
……//执行函数
……
isSuccess := true;
end;

优点:用户提供的是一组看上去无意义的二进制数,提高的攻击的难度。
缺点:函数中权限执行码是写死的,而且随着函数的增多,权限执行码会很长。

两种改进方法的结合:
组件模块初始化时从数据库中动态调出每一个函数的二进制编码,然后在函数中用
gloabRight.getRight()来取得保存在类中的二进制编码进行比较。这样可以在外部定时修
改函数的二进制编码,从而调整权限对应的权限执行码,这样当泄漏权限执行码时可以避免
长期的损失。而且避免了第一种改进方法中“当一个函数标志对应多个权限等级”时的麻烦



注:关于gloabRight.getRight()
gloabRight为模块内部的一个类,定义如下:
先定义一个结构体,用于放置函数标志和相应的执行码。
type RRright = record
functionID:String;
functionCord:Tstream;
//由于执行码可能很长,因此Delphi中用流来存储,
//具体编程语言自己决定类型
end;
type TgloabRight = class
private
Right: array of Rright;//动态数组,用于存放函数标志和相应执行码数组大
//小由函数表的记录数决定。
function getRight(const functionID:String;):TStream//按函数标志取得相应
//的执行码
public
constructor Create;//构造函数,从数据库中读取信息并初始化Right数组。
destructor Destroy;//析构函数,用于释放Right数组。
end;


用流来存取函数执行码是否可行?在数据库中怎么存储?

感谢您的耐心阅读!
代码仅供描述,有的地方不规范,见谅。
欢迎提供成功的权限管理的案例!
 
一般来说,应用程序的权限不太可能细分到每个函数中,使用不同的功能菜单来控制
权限的多一些,一般只需要将用户id,菜单项id,权限存储就可以了,权限又可大多
分为浏览,修改等
 
id password 级别
对于有针对单个用户的
登录成功后 将用户名 和 运行级别。如果没有针对个别用户的只用级别就可以
程序中可根据级别来屏蔽 相应功能,如果菜单按钮
或动态判断是否可执行。
 
建议您看一下所谓的 role based access control,
就是设立这样几个概念:
用户 - 用户组 - 脚色 - 权限 (User - User Group - Role - Permission)
最终,您要检查一个用户是否能具有某个权限,只要一连串 join 就可以了。
 
to yysun
请问哪里可以找到role based access control的具体资料?
 
http://csrc.nist.gov/rbac/
 
谢谢楼上各位。
还有话说的朋友请继续,过两天给分。
 
这是我自己的实现方法:欢迎指点!!
在Delphi中使用Action实现权限和日志管理
权限和日志管理是较为常见的水平功能,而且需求比较灵活,通常硬编码到程序中。
本文将对Delphi中的Action进行扩充实现将权限和日志管理功能。
1.
常见的方法是将权限管理和日志管理直接硬编码到过程或者对象的方法中。
例如:
procedure TForm1.Button1Click(Sender: TObject);
begin
if isSuperUser(UserName) then
begin
Close;
addLog(UserName,'Close');
end
else
showMessage('You have not this right');
end;
上面的代码将权限管理和日志管理硬编码到程序中,不利于程序的重用和扩展。
2.
下面是扩展的一个Action类Form1CloseAction,它封装了Close方法
TForm1CloseAction = class(TAction)
private
FOprName: string;
FUserName: string;
FForm: TForm1;
procedure SetForm(const Value: TForm1);
procedure SetOprName(const Value: string);
procedure SetUserName(const Value: string);
public
function Execute: Boolean;
override;
constructor Create(AOwner:TComponent);
override ;
published
property UserName:string read FUserName write SetUserName;
property OprName:string read FOprName write SetOprName;
property Form:TForm1 read FForm write SetForm;
end;
在Execute方法中完成用户的逻辑业务,这样可以很好的将应用程序的水平功能和垂直功能相分离。
function TForm1CloseAction.Execute: Boolean;
begin
result:= inherited Execute;
if haveRight(UserName,OprName ) then
begin
Form.Close;
addLog(UserName,'Close');
end
else
begin
showMessage('you have not this right');
end;
end;
注意到Form1CloseActiony有一个Form属性作为业务逻辑对象,所以在调用Execute前要初始化 Form属性。
这样我们只要动态创建一个 TForm1CloseAction对象CloseAction 并指定Button1.Action:=CloseAction。
3. 很明显通常一个业务逻辑对象有很多方法,如果按照上一步封装为Action的话,有很多重复编码。于是我们在TForm1CloseAction 和 Tacton之间插入一个类,并为这个类引入一个新的方法InternalExecute来封装用户的业务逻辑方法,使用Execute来完成水平功能。
TForm1Action = class(TAction)
private
FOprName: string;
FUserName: string;
FForm: TForm1;
procedure SetForm1(const Value: TForm1);
procedure SetOprName(const Value: string);
procedure SetUserName(const Value: string);
protected
procedure InternalExecute;
virtual;
abstract;
public
function Execute: Boolean;
override;
constructor Create(AOwner:TComponent) ;override ;
published
property UserName:string read FUserName write SetUserName;
property OprName:string read FOprName write SetOprName;
property Form:TForm1 read FForm write SetForm1;
end;
关键的方法Execute如下实现
function TForm1Action.Execute: Boolean;
begin
result:= inherited Execute;
if haveRight(UserName,OprName) then
begin
self.InternalExecute;
end
else
begin
showMessage('you have not this right ');
end;
addLog(UserName,'Close');
end;

这样原来的TForm1CloseAction 改为
TForm1CloseAction = class(TForm1Action)
protectec
procedure InternalExecute ;
override ;
public
constructor Create(AOwner:TComponent) ;override ;
end;

为简便起见我使用Form来表示业务逻辑对象,而实际的业务逻辑对象最好是于界面无关的,上述方法可以看作是MVC的子模式或实现。上面的模型还有很多可以扩展的方面,例如:将HaveRight,AddLog方法分别由权限管理类和日志管理类来实现权限信息和日志信息的持久化。这样Action就像粘合剂将业务逻辑,权限管理,日志管理整合在一起。
我最近看J2EE中有关Struct框架的介绍,发现有些相似的地方。
 
我用的是sqlserver,所以我把后台的权限放到前台来,
 
我用菜单控制
每个用户都的控制都不一样
由用户自己定义可操作的菜单项
 
精彩,继续。
 
如果需要根據用戶ID來進行區分權限而不設置權限級別,可以對某個用戶的權限進行設置
,那麼如何考慮才會有好的延展性。
 
我根据菜单来生成TreeView(VB中的),然后通过选择对TreeView的TreeNode多选来设置权限
 
建议用 组的概念
 
多人接受答案了。
 

Similar threads

D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
742
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
S
回复
0
查看
955
SUNSTONE的Delphi笔记
S
顶部