| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2101 人关注过本帖, 3 人收藏
标题:VFP 学习、开发漫谈 (27)- 权限管理
只看楼主 加入收藏
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:658
专家分:2180
注 册:2014-2-7
结帖率:96.88%
收藏(3)
 问题点数:0 回复次数:13 
VFP 学习、开发漫谈 (27)- 权限管理
今天与大伙儿聊一聊有关权限管理方面的话题。

下面将要介绍的权限管理方案有几个前提条件:

1. 所有功能模块都是针对表单的。也就是说,应用程序中的所有功能模块,都是通过表单来实现的。我相信,这样的处理方式能够满足绝大多数用户的要求。
2. 权限管理仅针对数据维护表单或需要保密的查询表单。对于那些一般的查询表单不进行权限控制。当然,如果你精力充沛,不怕麻烦,对所有表单都进行权限控制也没有任何问题。
3. 权限共分为三种:“读”、“写”和“无”。
“读”:是指用户可以打开表单,查看里面的数据,但不能修改数据(通过禁用“添加”、“修改”、“删除”按钮或其他控件来实现)。
“写”:是指用户可以不受任何限制地操作表单,不但可以查询数据,也可以修改数据。
“无”:表示用户不能打开表单,更不能查询和修改数据。实现的方法有两种:一种是禁用对应的菜单项和工具栏按钮,另一种是在表单的Init事件中查询用户对该表单的访问权限,若为“无”则Return .f.。本方案采用第一种方法。
4. 可以为“组”指定权限,也可以为“用户”指定权限。组成员自动继承组权限。当一个用户对同一个模块既拥有组权限又拥有用户权限时,忽略组权限。
5. 为了简化处理,本方案约定:每个用户仅隶属于一个组,且组之间不嵌套。

一、数据库设计

权限管理共涉及4个表:用户(user.dbf)、组(user_group.dbf)、模块(module.dbf)、权限(right.dbf)。

(一)用户表 User.dbf
 
图片附件: 游客没有浏览图片的权限,请 登录注册

说明:
● 用户名(Name):输入用户的姓名(汉字),若有重名,则后面的用户在姓名后加序号,如:“张三1”。
● 简拼(PinYin_Abb)”和“全拼(PinYin_All)”:设置这两个字段是为了在登录时,既可以输入姓名,也可以输入姓名的简拼或全拼,当离开焦点时再自动转换为用户名。
● 是否有效(Valid):用于暂停用户的登录权限。默认值是.t.

(二)用户组表 User_Group.dbf
图片附件: 游客没有浏览图片的权限,请 登录注册

(三)模块表 Module.dbf
 
图片附件: 游客没有浏览图片的权限,请 登录注册

说明:
● 菜单变量(mvar):表示该模块对应的菜单项,如:“2,sys”表示该模块对应“系统维护”菜单的第2个菜单项,用于根据权限启用或禁用菜单项。
● 工具栏变量(tvar):表示该模块对应的工具栏按钮,如:“cmdPorder”表示当前模块对应的工具栏按钮的名字是cmdPorder,用于根据权限启用或禁用工具栏按钮。
● 模块说明(desc):该模块的功能描述。

(四)权限表 Right.dbf
 
图片附件: 游客没有浏览图片的权限,请 登录注册

说明:
● 权限类型(user_type):用于区分当前记录所表示的权限类型,G表示组权限,U表示用户权限
● 权限(right):读,写,无。有些读者可能对这里的“无”有疑问。若不为用户分配权限,即:在权限表中无对应的记录,也可以表示权限为“无”。但是,当一个用户从组继承了某个模块的权限,而我们又想将此用户继承的组权限移除,该怎么办呢?解决方法就是再为该用户分配一个“无”权限,因为用户权限优先于组权限。这里的“无”相当于Windows 中的“拒绝”。

二、用户身份识别

在登录时,将“用户id(User_Id)”和“组id(Group_Id)”保存到全局变量 gnUserId和gnGroupId,这两个变量就是用户的身份标识,用于权限检测。为了方便用户登录,可以将本次成功登录的“用户id”保存起来,作为下次登录的默认用户。下次登录时,直接键入密码即可。

在我开发的系统中,还有一种身份识别方式:通过检测客户端的IP、MAC、计算机名,然后自动登录。这种方式适用于公司高管。因为公司高管拥有较高的审批权限,若账号被盗用,则后果严重。另一方面,若为其设置的密码过于复杂,则容易遗忘,并且让其定期更换密码也不现实。实际应用中,一个用户可以绑定多个IP、MAC、计算机名。因为多数高管都有2台电脑,一台笔记本,一台微机。

三、初始化菜单和工具栏

在成功登录系统后,应该按照权限表中的设置,启用或禁用对应的菜单项和工具栏按钮。在主程序中,通过执行 InitMenuToolbar(gnUserId,gnGroupID,oTbr) 来实现的。其中,gnUserId 为本次登录的用户id,gnGroupId为用户所属的组id,oTbr为工具栏对象。
程序代码:
* 初始化菜单、工具栏---------------------------------------------------------
FUNCTION InitMenuToolbar(nUserId,nGroupId,oToolbar) && 用户id,组id,工具栏对象
    LOCAL lValue
    * 打开模块表,先将菜单项与工具栏按钮禁用
    SELECT 0
    USE module ORDER m_id SHARED
    SCAN FOR !DELETED()
        = SetMenu(ALLT(module.mvar),.T.)            && 禁用菜单
        = SetToolbar(oToolbar,ALLT(module.tvar),.F.)&& 禁用工具栏
    ENDSCAN
    * 打开权限表,并设置与模块表关联
    SELECT 0
    USE right SHARED
    SET RELATION TO m_id INTO module
    * 先根据组权限设置菜单和工具栏
    IF nGroupId # 0
        SCAN FOR user_id = nGroupId AND user_type = 'G' AND !DELETED()
            lValue = (right # '')
            = SetMenu(ALLT(module.mvar),!lValue)
            = SetToolbar(oToolbar,ALLT(module.tvar),lValue)
        ENDSCAN
    ENDIF
    * 再根据用户权限设置菜单和工具栏
    SCAN FOR user_id = nUserID AND user_type = 'U' AND !DELETED()
        lValue = (right # '')
        = SetMenu(ALLT(module.mvar),!lValue)
        = SetToolbar(oToolbar,ALLT(module.tvar),lValue)
    ENDSCAN
    * 关闭表
    USE IN right
    USE IN module
ENDFUNC

* 子函数 - 设置菜单---------------------------------------------------------------
FUNCTION SetMenu(tcMenuName,tlValue)     && 菜单名,可用状态(.T. 不可用,.F. 可用)
    LOCAL i,cBar,cPop
    i = AT(',',tcMenuName)               && 【菜单条】与【弹出菜单】间以逗号分隔
    IF i # 0
        cBar = LEFT(tcMenuName,i-1)      && 菜单条
        cPop = SUBSTR(tcMenuName,i+1)    && 弹出菜单
        SET SKIP OF BAR &cBar OF (cPop) tlValue    && 设置菜单的可用状态
    ENDIF
ENDFUNC

* 子函数 - 设置工具条-------------------------------------------------------------
FUNCTION SetToolbar(oToolBar,tcToolName,tlValue) && 工具栏,工具按钮,设置值
    LOCAL cCtrl
    IF !EMPTY(tcToolName) AND TYPE('oToolBar.'+ tcToolName) = 'O'
        cCtrl = 'oToolBar.' + tcToolName
        &cCtrl..Enabled = tlValue
    ENDIF
ENDFUNC

四、运行表单时检测权限

以采购订单为例,在表单的 Init 事件中,执行如下代码:
程序代码:
LOCAL cRight
cRight = CheckRight('porder')    && 获取用户对本表单的访问权限
DO CASE
CASE cRight = ''
    && 拒绝访问
    MESSAGEBOX('你没有访问本模块的权限,请与管理员联系!',48,'提示')
    RETURN .f.
CASE cRight = ''
    && 只读访问:禁用“添加/修改/删除”功能
    THIS.cmdAdd.Enabled = .f.
    THIS.cmdEdit.Enabled = .f.
    THIS.cmdDelete.Enabled = .f.
ENDCASE

通用过程函数 CheckRight()的代码如下:
程序代码:
* 权限检查-------------------------------------------------------------------
FUNCTION CheckRight(tcModule)            && cModule(模块名)
    LOCAL nSelect,cRight,nMid,lFlag
    nSelect = SELECT()
    cRight = ''
    * 找到模块 tcModule 对应的模块号 nMid
    IF !USED('module')
        USE module SHARED IN 0
        lFlag = .t.
    ENDIF
    SELECT module
    LOCATE FOR ALLT(UPPER(Module)) == ALLT(UPPER(tcModule)) AND !DELETED()
    nMid = m_id
    IF lFlag
        USE IN module
    ENDIF
    * 先处理组权限
    lFlag = .f.
    IF !USED('right')
        USE right SHARED IN 0
        lFlag = .t.
    ENDIF
    SELECT right
    IF gnGroupId # 0
        LOCATE FOR user_id = gnGroupId AND user_type = 'G' AND m_id = nMid AND !DELETED()
        IF FOUND()
            cRight = right
        ENDIF
    ENDIF
    * 再处理用户权限
    LOCATE FOR user_id = gnUserId AND user_type = 'U' AND m_id = nMid AND !DELETED()
    IF FOUND()
        cRight = right
    ENDIF
    IF lFlag
        USE IN right
    ENDIF
    SELECT (nSelect)
    RETURN cRight
ENDFUNC

五、权限设置

由于权限设置表单的代码很多,下面仅介绍设计思路。具体代码我已经打包,有兴趣的朋友可以下载。
Right_Manage.RAR (14.42 KB)

1. 按“组”分配权限
图片附件: 游客没有浏览图片的权限,请 登录注册

通过把权限分配给用户组,则组成员自动继承这些权限,从而大大简化了权限设置的工作量。比如:仓库新来了个保管员,我只需为其新建一个用户,并将其添加到保管员组即可,无需再为其单独分配权限。

操作:首先,从上面的列表中选择一个组,则在右下方列表中显示该组所拥有的权限,而左下方列表中显示的是没有分配给该组的权限。通过中间的4个按钮或双击两个列表中的记录,则可以添加或移除权限,即:在左右两个列表中移动权限。

添加到右侧列表中的权限默认值是“读”,可以通过单击中间的“权限”列从组合框中选择其他权限。

2. 按“用户”分配权限
图片附件: 游客没有浏览图片的权限,请 登录注册

右下方列表中显示当前用户的所有权限,其中:红色字体显示的权限是从“组”继承过来的,不能修改。用户权限用黑色字体显示,可以修改和删除。可以通过为用户指定附加的用户权限来覆盖组权限。

这里介绍一个小技巧:对于权限列来说,红色字体的组权限不能修改,但是黑色字体的用户权限可以修改,如何才能做到这一点呢?方法是:在权限列的组合框控件的When事件中,输入:RETURN usr_b.u_type='U' 即可。当权限属于组时,When事件返回.f.,则列控件不能获得焦点,更不能修改,而When事件返回.t.时,可以正常操作。

3. 按“模块”分配权限
图片附件: 游客没有浏览图片的权限,请 登录注册

该方式可以查询某个功能模块哪些用户有操作权限,是前两种权限分配方式的完美补充。由于一个模块既可以分配到组,也可以分配给用户,所以较前两种分配方式稍复杂些。


[ 本帖最后由 liuxingang28 于 2014-7-26 07:32 编辑 ]
收到的鲜花
  • tlliqi2014-07-25 18:44 送鲜花  50朵   附言:谢谢
搜索更多相关主题的帖子: 应用程序 开发 保密 用户 
2014-07-25 16:56
tlliqi
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:204
帖 子:15453
专家分:65956
注 册:2006-4-27
收藏
得分:0 
好好学下
2014-07-25 18:45
hu9jj
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:红土地
等 级:贵宾
威 望:400
帖 子:11857
专家分:43421
注 册:2006-5-13
收藏
得分:0 
用户操作权限分组管理有一定的使用价值,但现实中似乎还无法满足要求,最好的办法是给每个操作者单独设定操作权限。

活到老,学到老!http://www.(该域名已经被ISP盗卖了)E-mail:hu-jj@
2014-07-26 07:10
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:658
专家分:2180
注 册:2014-2-7
收藏
得分:0 
回复 3 楼 hu9jj
我的方案是分组与用户相结合,也就是说:既可以将权限分配给组,也可以直接将权限分配给单独的用户,还可以先将用户加入组,在继承组权限的同时,再为其添加用户权限。

泉城飞狐
2014-07-26 07:37
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:658
专家分:2180
注 册:2014-2-7
收藏
得分:0 
示例下载中的代码有一个Bug,现已修复并重新上传。在表单的load事件中,添加了一行 set safety off,以解决 zap 时,不停弹出提示窗口的问题。

泉城飞狐
2014-07-26 07:42
race13
Rank: 1
等 级:新手上路
帖 子:17
专家分:0
注 册:2014-1-7
收藏
得分:0 
感谢分享。
2014-07-26 08:45
jsddx
Rank: 2
等 级:论坛游民
帖 子:44
专家分:25
注 册:2006-4-11
收藏
得分:0 
谢谢!学习!
2014-07-26 11:30
asdf_123000
Rank: 4
等 级:业余侠客
威 望:1
帖 子:273
专家分:227
注 册:2012-12-20
收藏
得分:0 
很有用,感谢!
2014-07-27 09:36
jacksonwong
Rank: 1
等 级:新手上路
帖 子:1
专家分:0
注 册:2010-12-20
收藏
得分:0 
有用
2014-10-11 17:45
qingfameng
Rank: 12Rank: 12Rank: 12
等 级:贵宾
威 望:35
帖 子:964
专家分:3019
注 册:2010-2-6
收藏
得分:0 
进来学习学习。但我用的是‘扑克牌’方式,一人一张牌。
2014-10-12 22:51
快速回复:VFP 学习、开发漫谈 (27)- 权限管理
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.052412 second(s), 11 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved