如鹏网 大学生计算机学习社区

CowNew开源团队

http://www.cownew.com 邮件请联系 about521 at 163.com

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  363 随笔 :: 2 文章 :: 808 评论 :: 0 Trackbacks
 

  近日(呵呵,这篇文章是去年写的)用了两个月开发了一个物流信息系统,这个系统是两层、三层相结合,C/SB/S相结合的系统。虽然限于时间的紧张和人手的原因,系统规模不是很大,但是其中涉及的技术却很全面。在这个《开发技术篇》中我们将讲解我在开发系统中遇到的技术问题及解决方案,希望对大家有帮助。对于物流信息系统的分析设计问题,我将在另一篇文章《物流信息系统开发手记――系统构架篇》中讲解。

 

一、Midas的安全问题。

    Midas技术是Delphi中进行三层开发的首选技术,它不仅有纯DCOM/COM+(COM+技术是.NET技术的基础)的优点,而且也结合了Delphi的快速开发特性,可以快速开发出想要的系统,其开发速度是用VC,PB等开发DCOM的数十倍,把程序员从烦杂的代码中解脱出来,从而将更多的精力投入到业务逻辑的设计中去。

    但是Midas技术的一个最令人担忧的就是它的安全问题:

远端只要知道应用服务器的端口号即可访问到应用服务器,而一旦访问到应用服务器,TClientDataSet即可获得ProviderNames列表。一旦知道了ProviderNames列表,这就相当于将数据库暴露在外了。

关于可轻易获得ProviderNames列表的问题,我使用下面的方法解决:

 在服务器端定义一个

LoginMTS(const AUserId, APassword: WideString): WordBool;

方法。初始状态下,所有的DataSetProvider和数据集的连接断开。用户必须调用LoginMTS并传递用户名和密码,登陆成功才将DataSetProvider和数据集的连接打开。这样如果用户验证没有通过,即使它获得了ProviderNames列表也没法调用接口中的方法对数据库进行操作。

二、Midas中主从表的实现

主从表的应用在信息系统中应用很广。在两层开发中我们可以通过直接建立两个数据集之间为主从关系来实现主从表;在三层中虽然我们仍然可以通过直接建立两个数据集之间为主从关系来实现主从表,但是这样就要求把数据库中所有相关的数据行都下载到本地,丧失了三层开发的优势。我在实际中使用下面的方法实现。这里我以实现入库单查询、添加、修改、删除(CRUD)为例来讲解:

1)新建一个MTS Data Module,命名为TmtsStockInListBiz,增加如下方法:

    function QueryStockInListMasterById(const AId: WideString;

      var ADatas: OleVariant): WordBool; safecall;

    function QueryStockInListSlaveByMasterId(const AId: WideString;

      var ADatas: OleVariant): WordBool; safecall;

    procedure UpdataStockInListMaster(var ADatas: OleVariant); safecall;

    procedure UpdataStockInListSlave(var ADatas: OleVariant); safecall;

    function GenerateStockInListId: WideString; safecall;

 

QueryStockInListMasterById作用是根据入库单单号查询入库单的基本信息(入库日期、负责人等),Aid为入库单单号,Adatas为返回值,其格式就是Midas的数据包,可以将其附给ClientDatSetData属性。

QueryStockInListSlaveByMasterId作用是根据入库单单号查询入库单的详细信息(商品条码,数量)

UpdataStockInListMaster是对入库单主表进行删除、添加、修改操作。只要将ClientDataSetDelta属性做为传递即可。

UpdataStockInListSlave是对入库单从表进行删除、添加、修改操作。

GenerateStockInListId是产生一个唯一的入库单号。

下面是几个方法的代码,都很简单,就不多解释了,可以查看Delphi的帮助。

function TmtsStockInListBiz.QueryStockInListMasterById(

  const AId: WideString; var ADatas: OleVariant): WordBool;

begin

  result := false;

  ADatas := null;

  try

    cdsQuery.Close;

    cdsQuery.CommandText := 'select * from t_StockInListMaster where Id=:Id';

    cdsQuery.Params.ParamByName('Id').AsString := AId;

    cdsQuery.Open;

    if cdsQuery.RecordCount > 0 then

    begin

      result := true;

      ADatas := cdsQuery.Data;

    end;

  finally

    cdsQuery.Close;

  end;

end;

 

procedure TmtsStockInListBiz.UpdataStockInListMaster(

  var ADatas: OleVariant);

var

  eCount: Integer;

  OwnerData: OleVariant;

begin

  DCOMConStockInList.GetServer.AS_ApplyUpdates('dspStockInListMaster',

    ADatas, -1, eCount, OwnerData);

end;

 

function TmtsStockInListBiz.GenerateStockInListId: WideString;

var

  LPrior: string;

  i: Integer;

begin

  cdsQuery.Close;

  cdsQuery.CommandText := 'select top 1 id from t_StockInListMaster order by id desc';

  cdsQuery.Open;

  LPrior := cdsQuery.FieldByName('Id').AsString;

  i := StrToIntDef(RightStr(LPrior,8),0);

  Inc(i);

  result := 'RK' + FormatFloat('00000000',i);

  cdsQuery.Close;

end;

 

2)、新建一个应用程序,通过DCOMConnectionSocketConnection等连接到MTS组件,然后就可以调用MTS的相应的方法实现客户端功能了。

放入cdsStockInListMastercdsStockInListSlave两个ClientDataSet控件,在控件上点击右键,选择“FieldsEditor”新建于服务器中的字段同样的字段,然后再次在控件上单击右键,选择“CreateDataSet”,建立一个本地数据库。

3

根据入库单号查询入库单的方法实现:

procedure TFormStockInList.BtnFindClick(Sender: TObject);

var

  v,vs: OleVariant;

begin

  if SocketConStockInList.AppServer.QueryStockInListMasterById(Trim(LEdtId.Text), v) then

  begin

    cdsStockInListMaster.Data := v;//显示入库单主表(主要信息)

 

    if SocketConStockInList.AppServer.QueryStockInListSlaveByMasterId(Trim(LEdtId.Text), vs) then

      cdsStockInListSlave.Data := vs; ;//显示入库单从表(明细信息)

  end

  else

    ShowMessage('此单不存在!');

end;

4)新建入库单的实现

procedure TFormStockInList.BtnNewClick(Sender: TObject);

var

  LId: string;

begin

  ClearCDSRecord;

  cdsStockInListMaster.Open;

  cdsStockInListMaster.Insert;

  LId := SocketConStockInList.AppServer.GenerateStockInListId;

  LEdtId.Text := LId;

  cdsStockInListMaster.FieldByName('Id').AsString := LId;

  cdsStockInListMaster.FieldByName('GenerateDate').AsDateTime := Now();

end;

5)提交功能的实现

procedure TFormStockInList.BtnPostClick(Sender: TObject);

var

  LQuerymts: ImtsQueryObjDisp;

  LBar: string;

begin

  SetSocketConnectionConnect(SocketConQuery);

  LQuerymts := ImtsQueryObjDisp(SocketConQuery.GetServer);

 

  SocketConQuery.Close;

 

  if cdsStockInListMaster.RecordCount > 0 then

    SocketConStockInList.AppServer.UpdataStockInListMaster(cdsStockInListMaster.Delta);

  if cdsStockInListSlave.RecordCount > 0 then

  SocketConStockInList.AppServer.UpdataStockInListSlave(cdsStockInListSlave.Delta);

end;

注:本文中ClientDataSet控件的名称开头一般为cdsTsocketConnection控件的名称开头一般为SocketCon

三、动态设置TsimpleObjectBroker的服务器列表

procedure SetSocketConnectionConnect(AValue: TSocketConnection);

  procedure FillAppServerList(ABroker: TSimpleObjectBroker);

  var

    sl: TStringList;

    i, n: Integer;

  begin

    sl := TStringList.Create;

    从配置文件中读取服务器列表,并保存到sl;

    n := sl.Count - 1;

    ABroker.ServerData := null;

    for i := 0 to n do

    begin

      ABroker.Servers.Add;

      ABroker.Servers[i].ComputerName := sl.Strings[i]

    end;

    sl.Free;

  end;

var

  LBroker: TSimpleObjectBroker;

begin

  LBroker := TSimpleObjectBroker.Create(nil);

    FillAppServerList(LBroker);

    AValue.ObjectBroker := LBroker;

    try

      AValue.Connected := true;

    except

      raise Exception.Create('应用服务器连接错误!');

    end;

    LBroker.Free;

end;

posted on 2005-10-22 00:51 CowNew开源团队 阅读(2253) 评论(1)  编辑  收藏

评论

# re: 物流信息系统开发手记-Delphi的三层开发 2009-12-12 15:43 hello
想请教下Midas的安全问题,我认为,

如果有一个软件的正常用户用LoginMTS()登录之后,DataSetProvider和DataSet就关联起来了。

之后其它非法用户再连接上此端口时,即使不需要LoginMTS(),DataSetProvider和DataSet也已经关联,非法用户也就可以访问应用层的DataSet了。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: