在运行应用程序时,首先看到的就是用户登录界面,只有输入正确的用户名及密码,才可以进入程序的主界面。用户登录的作用是保护应用程序中的相关资料,以防止无关人员对程序信息的肆意破坏。当进入程序后,会根据用户的所拥有的权限对程序中的相关模块进行相应操作。为了防止其他人员在数据库中获取用户信息,而对应用程序进行操作,可以对用户名和密码进加密后存入数据库中。本章将详细介绍如何制作用户的登录界面、权限以及密码验证。
3.1 基础技术分析
本节将对用户登录、权限及密码验证在制作过程中所应用的基础知识进行一下讲解,使读者在制作登录窗体及用户权限时便于编写。
3.1.1 用户登录分析
在制作用户登录窗口时,最基本的就是如何判断用户名和密码是否在指定数据表的同一个记录中,如果在,表示该用户可以登录该系统,反之,用户名、密码不正确。
在数据表中判断用户名及密码是否正确,可以用SQL语句来实现。
首先,在指定的数据表中查找是否有要登录的用户名。
select * from 表名 where 用户字段名=用户名称
如果有用户名,则判断输入密码是否与该记录中的密码相同。
If 记录>0
Begin
If 当前记录的密码字段信息=输入密码
登录成功
Else
密码错误
End
Else
用户名不正确
以上的登录过程,适合于ADO组件和BDE组件。
在制作管理软件时,登录窗口最好设为子窗体,因为登录窗体关闭时,不一定关闭整个工程。那么如何解决登录窗口的关闭问题呢?登录窗口有两种关闭情况,一种是自身窗体的关闭,进入主窗体;另一种是工程的关闭。工程关闭可以用如下代码来实现:
Application.Terminate;
而自身窗体的关闭只需要用窗体的Close方法便可以实现,具体如何关闭登录窗体可以用公共变量进行判断。
当然,登录窗体也可以是主窗体,在登录成功后,可以隐藏登录窗体,并显示主界面窗体,如果想要利用主界面窗体来关闭整个工程,可以在主界面窗体的OnClose事件中用Application.Terminate代码来关闭。
3.1.2 密码验证分析
密码验证实际上就是将密码进行加密,避免其他无关人员通过数据库,查找到用户数据表进入本系统。
密码加密可以有很多种方法,如在数据库中进行加密、对字符串进行算数加密、MD5加密等。在这里只对数据库加密及算数加密进行说明。
数据库加密是用SQL函数pwdencryp(@Str)对存入数据库中的字符串进行加密,其中@Str变量表示要加密的字符串密码。在登录时,通过Pwdcompare()函数来进行解密,其具体格式如下:
Pwdcompare(@Old,PassWord)
当该函数返回1时,表示旧密码与解密后的密码相同,如果返回0,表示不同。
l @Old:旧密码。
l PassWord:加密后的密码。
算数加密是将要加密的字符串中的字符按照指定的二进制数组进行异或运算,然后用IntToHex函数将异或后的数字转换成指定位数的字符串。当进行解密时,也是按照相同的二进制数组将加密后的字符串按照指定位数的字符串顺序进行异或运算,将其进行解密。
下面是IntToHex函数的语法格式:
function IntToHex(Value: Int64; Digits: Integer): string; overload;
该函数将一个数字转换成为一个字符串,字符串中包含该数字的十六进制表示形式。
IntToHex函数的参数说明如表3.1所示。
表3.1 IntToHex函数的参数说明
参数 |
说明 |
Value |
将要转换的整型值 |
Digits |
转换位数 |
返回值:返回转换后的字符串。
3.1.3 用户权限分析
用户权限实际上就是在用户登录后,是否可以在应用程序中调用各个子窗体,或者是在进入子窗体后,是否可以对该窗体中的各个组件进行操作。这样,可以将用户权限分成两种:一是对窗体进行控制;二是对窗体中的各个组件进行控制。
1.对窗体权限进行控制
只对窗体权限进行控制,可以在数据库中创建用户数据表时,插入与窗体个数等同的字段数,字段名的后面最好以数字进行排序(权限字段名如:P_0、P_1等),以便查找相应的字段,为了方便控制窗体,可以将字段类型设为Bit型,该类型只有两个值1(True)和0(False),可以通过该值来判断窗体是否可用。
在用户权限窗体中可以用TCheckBox组件来显示和设置权限,它是一个多选组件,可以通过用户数据表中的权限字段值对该组件的Checked属性进行设置。
用TCheckBox组件显示各窗体的权限,关键在于各窗体的权限字段如何与TCheckBox组件一一对应。可以用窗体的FindComponent()方法来查找窗体中已有的TCheckBox组件,并对其按照数据表的权限值进行设置。代码如下:
with ADOQuer do
begin
close;
SQL.Clear;
SQL.Add('select * from 用户表 where 用户字段名='+''''+查找的用户名称+'''');
Open;
for i:=0 to 12 do
begin
(self.FindComponent('CheckBox'+inttostr(i+1)) as TCheckBox).Checked :=
FieldByName('P_'+IntToStr(i)).AsBoolean;
end;
用户的权限名称是以权限名+数字进行组合的,可以通过数字将TCheckBox组件与权限相对应。
2.对窗体中的各个按钮进行控制
在对窗体中的各组件进行权限设置时,可以使用TTreeView组件对窗体以及窗体中各组件的使用权限进行设置。
在制作用户权限窗体前,首先要在数据库中创建一个数据表(tb_TemPop),该数据表记录了主窗体中的所有菜单名称,以及各菜单所对应窗体中的所有组件名称和使用权限等。数据表的关系图如图3.1所示。
图3.1 tb_TemPop数据表的关系图
可以通过tb_TemPop数据表查找出各主菜单下子菜单所对应窗体的各组件的应用权限。下面以tb_TemPop数据表中的信息作详细说明。如图3.2所示。
图3.2 tb_TemPop数据表
在tb_TemPop数据表中可以看出,当Fu_ID字段的值为0时,表示该记录为主菜单项,当Fu_ID字段的值为主菜单项的Zhu_ID字段值时,表示该记录为指定主菜单项的子菜单项(也就是子窗体的名称),当Fu_ID字段的值为子菜单项的Zhu_ID字段值时,表示该记录为子菜单项所对应窗体中的组件,在Tree_Name字段中记录了各窗体与组件的名称,便于在程序中查找相对应的各菜单项及相关组件
下面以TTreeView组件在权限窗体中应用的基础识知进行一下讲解。
在窗体显示时,可以利用TreeView1组件的Items属性的AddChild()方法向该组件中添加一个新节点。语法如下:
function AddChild(Node: TTreeNode; const S: string): TTreeNode;
Node:表示父节点,当Node为Nil,添加的是一个根节点,当Node不为空时,创建一个新的节点,是Node节点的子节点。
S:创建节点的名称。
返回值:当前添加节点的节点值。
在TreeView1组件中添加节点应用如下:
Var
ItemNode : TtreeNode;
begin
ItemNode := TreeView1.Items.AddChild(Nil,节点名称); //添加根节点
TempNode.ImageIndex := 0; //对根节点的图片样式进行设置
ItemNode := TreeView1.Items.AddChild(ItemNode,节点名称); //在根节点下添加子节点
TempNode.ImageIndex := 1; //对子节点的图片样式进行设置
end
如果要想在添加节点的同时,在节点的左面显示图片,或是以图片的形式来显示多选按钮,可以在TreeView1组件的Images属性中添加ImageList1组件,在ImageList1组件中保存了要显示的图片样式,在添加节点时,可以用当前节点的ImageIndex属性来设置要显示图片的索引号。
当根节点中的子节点过多时,可以在TreeView1组件中只显示根节点的信息,并在根节点的下面添加一个空的子节点,这样,可以在根节点的左面出现一个扩展标记,以便在扩展根节点时,动态加载当前根节点的子节点,这一操作可以在TreeView1组件的OnExpanding事件中完成,该事件的语法如下:
procedure TreeView1Expanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
在该语法中的Node参数记录了当前扩展节点的节点值,可以通过Node节点值,在该节点的下面添加子节点。在添加子节点前,用当前节点的DeleteChildren属性删除该节点下的所有子字点,如Node.DeleteChildren。因为在添加根节点时,在根节点的下面添加了一个空的子节点。
添加完子节点后,就可以通过鼠标选择节点来改变节点左面的图片样式。为了防止用鼠标扩展节点时,改变图片的样式,可以用单击鼠标右键来完成图片的更换。该操作可以在TreeView1组件的OnMouseUp事件中完成。代码如下:
procedure TFrm_Popedom.TreeView1MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
begin
if Button = mbRight then
begin
//更换图片或其它的操作
end;
在扩展和收缩节点时,可以利用OnGetImageIndex事件来改变当前节点和子节点的图片样式。语法格式如下:
procedure TreeView2GetImageIndex(Sender: TObject;
Node: TTreeNode);
Node参数记录了当前操作的节点值。可以通过该节点的Level属性来判断当前节点是根级节点还是子级节点,当Level属性值为0时,表示根级节点,当Level属性值大于0时,表示子级节点。可以通过Level属性来设置Node节点或Node子节点的图片样式。代码如下:
procedure TFrm_User_Part.TreeView2GetImageIndex(Sender: TObject;
Node: TTreeNode);
begin
if node.Level = 0 then
node.ImageIndex := 1
else
if Node.Level = 1 then
node.ImageIndex := 2;
end;
在TreeView1组件中选中一个节点时,改变当前节点的图片样式,可以在OnGetSelectedIndex事件中完成。其实现过程与OnGetImageIndex事件的实现过程基本相同,在这里不作讲解。