1/1页1 跳转到查看:2717
发新话题 回复该主题

苦恼多时的COM+远程调用问题

苦恼多时的COM+远程调用问题

COM+真是烦。。。

想实现COM+的远程调用。
环境如下:
1、服务端WINXP SP2 局域网通过路由器共享上网
2、COM+安装服务端上。
3、客户端在任意一台外网可连接Internet的电脑上。通过服务端路由的IP进行访问。


目前出现的问题是提示: RPC服务器不可用

另:在同一台电脑上安装服务端数据库、COM+、客户端软件都装在同一步电脑上调用正常。。

我觉得可能是两个原因:
1、由于IP不在我的服务端电脑上,而在路由器上,所以外网电脑无法访问到。
2、访问到了可能是安全检查不通过,如用户不正确等等。

TOP

 

回复:苦恼多时的COM+远程调用问题

windwos的安全机制规定:windows接收远程的com+调用的时候,会验证这个调用的权限。如果权限不够就出现经典的“拒绝访问”错误。

解决这个问题已知访问方式有:

1、匿名访问;在应用服务器(简称AP)启用guest,并且设置guest具有激活和访问COM+的权限。这条路是可行,不过安全性不能得到保证。

2、客户端电脑的登录用户和密码和AP上的一个用户一致,并且这个用户在AP上也具有相应的访问COM+的权限。这种方式要比第一种好一些,但是哪个单位的IT系统会是这种样子呢。肯定是每台机器都有自己的帐户密码。这个方式也不好。注:这种方式在不需要发布客户端软件到诺干多的机器上的时候也是可行的,比如访问COM+ 的是webServer.

3、在域管理的网络环境中,同样可以实现,但是有个问题,如果您的客户不愿意改造成域环境呢。所以这种方法也是有局限性的。

最后,我想到如果远程访问COM+的时候能够显式的给定用于AP验证权限的用户名和密码不是就可以解决这个问题了吗?事实上这个方式是可行的。不过在delphi7中还没有现成的函数可以达到这个目的。

comobj.pas 中有个函数 function CreateRemoteComObject(const MachineName: WideString;
  const ClassID: TGUID): IUnknown; 这个是用来创建远程的com+接口的。我要改造的主要是这个函数。



function CoCreateInstanceEx(const clsid: TCLSID;
  unkOuter: IUnknown; dwClsCtx: Longint; ServerInfo: PCoServerInfo;
  dwCount: Longint; rgmqResults: PMultiQIArray): HResult; stdcall; 这个函数可以用来创建远程的com+

ServerInfo 用来存储远程的服务器信息,包括访问和激活com+服务的帐户和密码。

我们来分析一下PCoServerInfo;

PCoServerInfo = ^TCoServerInfo;
  _COSERVERINFO = record
    dwReserved1: Longint;
    pwszName: LPWSTR;
    pAuthInfo: Pointer;
    dwReserved2: Longint;
  end;



    pUnShort=^Word;
    pCoAuthIdentity=^_CoAuthIdentity;
    _CoAuthIdentity=record
        user:pUnShort;
        UserLength:ULONG;
        Domain:pUnShort;
        DomainLength:Ulong;
        password:pUnShort;
        PasswordLength:ulong;
        Flags:ulong;
    end;
    _CoAuthInfo=record
        dwAuthnSvc:WORD;
        dwAuthzSvc:WORD;
        pwszServerPrincName:WideString;
        dwAuthnLevel:word;
        dwImpersonationLevel:dword;
        pAuthIdentityData:pCoAuthIdentity;
        dwCapabilities:WORD;
    end;
    TSocInfo=class(Tobject)
    public
        fcid:_CoAuthIdentity;
        fcai:_CoAuthInfo;
        ServerInfo: TCoServerInfo;
    end;


我们在CreateRemoteComObject中调用CoCreateInstanceEx的时候,首先给ServerInfo赋值如下

function CreateRemoteComObjectwh(const MachineName: WideString;
  const ClassID: TGUID): IUnknown;
const
  LocalFlags =CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER or CLSCTX_INPROC_SERVER;
  RemoteFlags = CLSCTX_REMOTE_SERVER;
var
    MQI: TMultiQI;
    ServerInfo: TCoServerInfo;
    IID_IUnknown: TGuid;
    Flags, Size: DWORD;
    LocalMachine: array [0..MAX_COMPUTERNAME_LENGTH] of char;
  ////  add by wanghui 2007-07-24
    Fcai:_CoAuthInfo;
    Fcid:_CoAuthIdentity;
    wUser,wDomain,wPsw:WideString;
    iiu:idispatch;
    fr:HRESULT;
begin
  if (GetObjectContext = nil)  then
  begin
        if @CoCreateInstanceEx =nil then
            raise Exception.CreateRes(@SDCOMNotInstalled);

    wUser:=getAppUserid();//用户名
    wDomain:=getappserver();//远程计算机名
    wPsw:=getAppPassword();//密码

    FillMemory(@Fcai,sizeof(Fcai),0);
    FillMemory(@FCid,sizeof(FCid),0);

    with fcid do begin
        user:=pUnshort(@wUser[1]);
        UserLength:=length(wUser);
        Domain:=pUnshort(@wDomain[1]);
        DomainLength:=length(wDomain);
        password:=pUnshort(@wPsw[1]);
        PasswordLength:=length(wPsw);
        Flags:=2;
    end;

    with fcai do begin
        dwAuthnSvc:=10;//winNt默认的鉴证服务  RPC_C_AUTHN_WINNT
        dwAuthzSvc:=$FFFFFF;//0;            //RPC_C_AUTHZ_NONE
        //pwszServerPrincName:=pwidechar(wDomain);
        dwAuthnLevel:=3;//0;
        dwImpersonationLevel:=3;//必须设置成模拟
        pAuthIdentityData:=@fcid;
        dwCapabilities:=$0;//$0800;
    end;
    FillMemory(@ServerInfo, sizeof(ServerInfo), 0);
    ServerInfo.pwszName := PWideChar(wDomain);
    ServerInfo.dwReserved1:=0;
    ServerInfo.pAuthInfo:=@fcai;

    IID_IUnknown := IUnknown;
    MQI.IID := @IID_IUnknown;
    MQI.itf := nil;
    MQI.hr := 0;

    if Length(MachineName) > 0 then
    begin
        Size := Sizeof(LocalMachine);  // Win95 is hypersensitive to size
        if GetComputerName(LocalMachine, Size) and  (AnsiCompareText(LocalMachine, MachineName) = 0) then
          Flags := LocalFlags
        else
          Flags := RemoteFlags;
    end else Flags := LocalFlags;
    OleCheck(CoCreateInstanceEx(ClassID, nil, CLSCTX_REMOTE_SERVER, @(ServerInfo), 1, @MQI));
    OleCheck(MQI.HR);
    Result := MQI.itf;
  end  else
  begin
        GetObjectContext.CreateInstance(ClassID, IUnknown, Result);
  end;
end;




以上代码 确保获取远程的com+的接口,接口类型为Iunkown



但是要访问其中的方法还需要用下面的函数来设置远程com本地引用的访问权限。

with fcai do
        CoSetProxyBlanket(iu,dwAuthnSvc,dwAuthzSvc,pwidechar(pAuthIdentityData^.Domain),
            dwAuthnLevel,dwImpersonationLevel,pAuthIdentityData,dwCapabilities);

将这个函数封装后得到一个新函数

function SetProxyBlanket(iu:IUnknown):boolean;
var
    Fcai:_CoAuthInfo;
    Fcid:_CoAuthIdentity;
    wUser,wDomain,wPsw:WideString;
    iiu:idispatch;

    si:Tsocinfo;
begin

  wUser:=getAppUserid();//用户名
    wDomain:=getappserver();//远程计算机名
    wPsw:=getAppPassword();//密码
  if wDomain='127.0.0.1' then exit;
    FillMemory(@Fcai,sizeof(Fcai),0);
    FillMemory(@FCid,sizeof(FCid),0);
    // FillMemory(@FSvInfo,sizeof(FSvInfo),0);
    with fcid do begin
        user:=pUnshort(@wUser[1]);
        UserLength:=length(wUser);
        Domain:=pUnshort(@wDomain[1]);
        DomainLength:=length(wDomain);
        password:=pUnshort(@wPsw[1]);
        PasswordLength:=length(wPsw);
        Flags:=2;      //SEC_WINNT_AUTH_IDENTITY_UNICODE
    end;
    //以上填充_CoAuthIdentity结构
  with fcai do begin
        dwAuthnSvc:=10;//winNt默认的鉴证服务  RPC_C_AUTHN_WINNT
        dwAuthzSvc:=$FFFFFF;//0;            //RPC_C_AUTHZ_NONE
        //pwszServerPrincName:=pwidechar(wDomain);
        dwAuthnLevel:=3;//0;
        dwImpersonationLevel:=3;//必须设置成模拟
        pAuthIdentityData:=@fcid;
        dwCapabilities:=$0;//$0800;
    end;

      with fcai do
        CoSetProxyBlanket(iu,dwAuthnSvc,dwAuthzSvc,pwidechar(pAuthIdentityData^.Domain),
            dwAuthnLevel,dwImpersonationLevel,pAuthIdentityData,dwCapabilities);
end;



最后我们改造delphi自动生成的*_TLB.pas 中的函数CreateRemote 如下

class function Comymenu.CreateRemote(const MachineName: string): Imymenu;
var  iu:IUnknown;
begin
    iu:=CreateRemoteComObjectwh(MachineName, CLASS_mymenu);
    SetProxyBlanket(iu);
    result:=iu as Imymenu;
    SetProxyBlanket(IUnknown(result));
end;





参考了一些资料:MSDN,《windows安全性编程》。

软件环境:

  client  winXP SP2

AP: win2003 sp1




Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1711546
最后编辑5207 最后编辑于 2007-09-19 15:57:51

TOP

 

回复: 苦恼多时的COM+远程调用问题



引用:

DELPHI程序员开发com+应用的速度是非常快的,其主要原因是其较好地封装了com+的windows底层功能,开发人员通过较为简单的类继承就避开了复杂的com+底层技术细节,使开发人员将精力放在应用本身的功能上面。Delphi在封装com+应用时采取了许多折衷,在保留通用性的同时也避开了一些实现起来困难但是应用面不太广的com+底层特性。这些避开的特性中最令delphi com开发人员关心的就是安全特性。从delphi 5开始,有许多人都面临过这样的问题:com应用开发出来并且在本机上运行一切正常,但是一旦分发出去实施远程访问时,就无法正常运行了。我自己有段时间在看到“拒绝访问”错误提示时会本能的头晕。其实认真追究起来,还是因为自己对windows安全技术了解不多造成的。多年来我一直没有发现国内有windows安全方面比较系统的资料和书籍,直到Keith Brownr的<windows安全性编程>中文版的出现。正是基于这本书我才有了下面的一些试验,也知道了为什么我老是补拒绝的原因。下面的讨论只是我在解决自身现有代码的安全访问问题时,总结出的几个小经难方法。建议愿意了解windows安全性的朋友去看一看<windows安全性编程>一书,你会发现windows的安全不再神秘。
这篇文章将会说明如何以远程工作站上的用户身份激活com+对象,并以此用户身份访问Interface。
1、        Delphi默认com+对象的远程激活
Delph中远程com+对象激活一般通过TdispatchConnection及其子类来实现,实际代码中多用TDCOMConnection或TsocketConnectoion这两个组件,TDCOMConnection组件最终调用CoCreateInstanceEx创建com+对象。

CoCreateInstanceEx (const clsid: TCLSID; unkOuter: IUnknown; dwClsCtx: Longint; ServerInfoCoServerInfo;dwCount: Longint; rgmqResults: PMultiQIArray): HResult。


TDCOMConnection在调用CoCreateInstanceEx时为pCoServerInfo参数中的pAuthInfo传递了Null值,因此TdcomConnection在创建Com对象时使用的是本地计算机登录者的用户令牌。假若A计算机上的登录用户Auser使用TDCOMConnection类连接远程计算机B上的com+对象,则B计算机会使用Auser的用户名/密码在B计算机上建立登录会话并最终创建com+对象。但是一台windows工作站上的本地用户只能在本地登录而无法在别的计算机上登录,因此A计算机上的Auser就无法在B工作站上建立登录会话,当然也就无法创建com+对象,此时远程工作站B会尝试用Guest帐户建立会话并使用该账户激活com+对象。在这种情况下,如果B工作站上的Guest账户没有启用或Guest没有激活com+对象的权限,你就会看见令人头晕的提示“拒绝访问”。看到这里你是不对现在网上最“流行”的dcom配置方法有所悟了呢。那个方法就是允许everyone访问、激活com对象、并且将“默认身份验证级别”设置成无。这种方法能够使你的com应用可以“用了”,但是,它可以上“任何人”访问。而且这种设置你将无法利用com+基于角色的安全访问控制功能。
  2、怎样不用GUEST账户激活
这个问题的实际上应该是:怎样用远程工作站上的用户激活远程com对象。解决这个问题其实很简单:只要你在调用CoCreateInstanceEx时为它指定远程工作站上的用户名和密码,只要用户名/密码通过远程计算机的验证,并且该用户被授予了“远程激活”com+对象的权限,那么远程工作站会用该用户身份激活com+对象。看一下代码:

var


  mts:IMTSXjpimsDB;


  ov:Variant;


  i:integer;


  cai:_CoAuthInfo;


  cid:_CoAuthIdentity;


  csi:COSERVERINFO;


  mqi:MULTI_QI;


  iid_unk:TGUID;


  idsp:IDispatch;


  wUser,wDomain,wPsw:WideString;


begin


  wUser:=eduser.text;//用户名


  wDomain:=edSvr.Text;//远程计算机名


  wPsw:=edPsw.Text;//密码


  cid.user:=pUnshort(@wUser[1]);


  cid.UserLength:=length(wUser);


  cid.Domain:=pUnshort(@wDomain[1]);


  cid.DomainLength:=length(wDomain);


  cid.password:=pUnshort(@wPsw[1]);


  cid.PasswordLength:=length(wPsw);


  cid.Flags:=2;


  //以上填充_CoAuthIdentity结构


  cai.dwAuthnSvc:=10;//winNt默认的鉴证服务


  cai.dwAuthzSvc:=0;


  cai.pwszServerPrincName:=wDomain;


  cai.dwAuthnLevel:=0;


  cai.dwImpersonationLevel:=3;//必须设置成模拟


  cai.pAuthIdentityData:=@cid;


  cai.dwCapabilities:=$0800;


  //以上填充_CoAuthInfo结构
  FillChar(csi, sizeof(csi), 0);


  csi.dwReserved1:=0;


  csi.pwszName:=pwidechar(wdomain);


  csi.pAuthInfo:=@cai;


  //以上填充COSERVERINFO结构


  iid_unk:=IUnknown;


  mqi.IID:=@iid_unk;mqi.Itf:=nil;mqi.hr:=0;


  Screen.Cursor:=crHourGlass; olecheck(CoCreateInstanceEx(CLASS_MTSXjpimsDB,nil,CLSCTX_REMOTE_SERVER,@csi,1,@mqi));


这段代码中除了最后实际调用CoCreateInstanceEx外,前面的代码都是设置参数。这些参数的含义请大家参考msdn,除了用户名、主机名、密码外,只有一个重要要部分要说明:cai.dwImpersonationLevel必须设置成允许模拟(值为3),否则远程计算机将无法按提供的用户/密码建议网络会话。
3、不修改现有代码,可以实现用远程用户身份激活吗?
当然可以,我扩展了TDcomConnection类,为其加入了用户名和密码,并修改其默认的DoConnect方法,使其在调用CoCreateInstanceEx时用指定的用户名和密码填充参数。代码如下:


unit SecDComConnection;

interface
uses
  windows,SysUtils, Classes,ActiveX, DB, DBClient, MConnect,comobj,Midas;

type

{typedef struct _SEC_WINNT_AUTH_IDENTITY
unsigned short __RPC_FAR* User;
  unsigned long UserLength;

  unsigned short __RPC_FAR* Domain;

  unsigned long DomainLength;

  unsigned short __RPC_FAR* Password;

  unsigned long PasswordLength;

  unsigned long Flags;

SEC_WINNT_AUTH_IDENTITY, *PSEC_WINNT_AUTH_IDENTITY;
}

  {typedef struct _COAUTHIDENTITY

    USHORT * User;

    ULONG UserLength;

    USHORT * Domain;

    ULONG DomainLength;

    USHORT * Password;

    ULONG PasswordLength;

    ULONG Flags;

COAUTHIDENTITY;}



{#define RPC_C_AUTHN_NONE            0

#define RPC_C_AUTHN_DCE_PRIVATE    1

#define RPC_C_AUTHN_DCE_PUBLIC      2

#define RPC_C_AUTHN_DEC_PUBLIC      4

#define RPC_C_AUTHN_GSS_NEGOTIATE  9

#define RPC_C_AUTHN_WINNT          10

#define RPC_C_AUTHN_GSS_SCHANNEL  14

#define RPC_C_AUTHN_GSS_KERBEROS  16

#define RPC_C_AUTHN_MSN            17

#define RPC_C_AUTHN_DPA            18

#define RPC_C_AUTHN_MQ            100

#define RPC_C_AUTHN_DEFAULT      0xFFFFFFFFL
}

{#define RPC_C_AUTHZ_NONE      0

#define RPC_C_AUTHZ_NAME      1

#define RPC_C_AUTHZ_DCE      2

#define RPC_C_AUTHZ_DEFAULT  0xFFFFFFFF }


{
#define RPC_C_AUTHN_LEVEL_DEFAULT        0

#define RPC_C_AUTHN_LEVEL_NONE            1

#define RPC_C_AUTHN_LEVEL_CONNECT        2

#define RPC_C_AUTHN_LEVEL_CALL            3

#define RPC_C_AUTHN_LEVEL_PKT            4

#define RPC_C_AUTHN_LEVEL_PKT_INTEGRITY  5

#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY    6 }


{SEC_WINNT_AUTH_IDENTITY_UNICODE=2 }

  pUnShort=^Word;

  pCoAuthIdentity=^_CoAuthIdentity;

  _CoAuthIdentity=record

    user:pUnShort;

    UserLength:ULONG;

    Domain:pUnShort;

    DomainLength:Ulong;

    password:pUnShort;

    PasswordLength:ulong;

    Flags:ulong;
  end;


  _CoAuthInfo=record
    dwAuthnSvcWORD;
    dwAuthzSvcWORD;

    pwszServerPrincName:WideString;

  dwAuthnLevelword;


    dwImpersonationLevel:dword;


    pAuthIdentityData:pCoAuthIdentity;


    dwCapabilitiesWORD;


  end;

  TSecDComConnection = class(TDCOMConnection)
  private
  FCai:_CoAuthInfo;
  FCid:_CoAuthIdentity;
  FSvInfo:COSERVERINFO;
  FUser:WideString;
  FPassWord:WideString;
  procedure SetPassword(const Value: wideString);
  procedure SetUser(const Value: wideString);
  procedure SetSvInfo(const Value: COSERVERINFO);
  protected
    procedure DoConnect; override;
  public
    property SvInfo:COSERVERINFO read FSvInfo write SetSvInfo;
    constructor Create(AOwner: TComponent); override;
    procedure MySetBlanket(itf:IUnknown;const vCai:_CoAuthInfo);
    function GetServer: IAppServer; override;
  published
    property User:wideString read FUser write SetUser;
    Property Password:wideString read FPassword write SetPassword;
  end;
procedure Register;

implementation

constructor TSecDCOMConnection.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FillMemory(@Fcai,sizeof(Fcai),0);

  FillMemory(@FCid,sizeof(FCid),0);

  FillMemory(@FSvInfo,sizeof(FSvInfo),0);
  with FCai do begin

    dwAuthnSvc:=10;//RPC_C_AUTHN_WINNT
    dwAuthzSvc:=0;// RPC_C_AUTHZ_NONE
    dwAuthnLevel:=0;//RPC_C_AUTHN_LEVEL_DEFAULT
    dwImpersonationLevel:=3;
    pAuthIdentityData:=@fcid;
    dwCapabilities:=$0800;
  end;
end;

procedure TSecDCOMConnection.DoConnect;
var
  tmpCmpName:widestring;
  IID_IUnknown:TGUID;
  iiu:IDispatch;
  Mqi:MULTI_QI;
  qr:HRESULT;
begin
  if (ObjectBroker) <> nil then
  begin
    repeat
      if ComputerName = '' then
        ComputerName := ObjectBroker.GetComputerForGUID(GetServerCLSID);
      try
        SetAppServer(CreateRemoteComObject(ComputerName, GetServerCLSID) as IDispatch);
        ObjectBroker.SetConnectStatus(ComputerName, True);
      except
        ObjectBroker.SetConnectStatus(ComputerName, False);
        ComputerName := '';
      end;
    until Connected;
  end
  else if (ComputerName <> '') then
    begin
      with fcid do begin
        user:=pUnshort(@fuser[1]);
        UserLength:=length(fuser);
        tmpCmpName:=ComputerName;
        Domain:=pUnshort(@tmpCmpName[1]);
        DomainLength:=length(TmpCmpName);
        password:=pUnShort(@FPassword[1]);
        PasswordLength:=length(FPassword);
        Flags:=2;//Unicode
      end;
      FSvInfo.pwszName:=pwidechar(tmpCmpName);
      FSvinfo.pAuthInfo:=@Fcai;
      IID_IUnknown:=IUnknown;
      mqi.IID:=@IID_IUnknown;mqi.Itf:=nil;mqi.hr:=0;
      olecheck(CoCreateInstanceEx(GetServerCLSID,nil,CLSCTX_REMOTE_SERVER,@FSvinfo,1,@mqi));
      olecheck(mqi.hr);
      MySetBlanket(mqi.Itf,Fcai);
      qr:=mqi.Itf.QueryInterface(idispatch,iiu);
      olecheck(qr);
      MySetBlanket(IUnknown(iiu),FCai);
      SetAppServer(iiu);
    end
    else
      inherited DoConnect;
end;

function TSecDComConnection.GetServer: IAppServer;
var
  QIResult: HResult;
begin
  Connected := True;
  QIResult := IDispatch(AppServer).QueryInterface(IAppServer, Result);
  if QIResult <> S_OK then
  begin
    Result := TDispatchAppServer.Create(IAppServerDisp(IDispatch(AppServer)));
  end;

  MySetBlanket(IUnknown(Result),FCai);
end;

procedure TSecDCOMConnection.MySetBlanket(itf: IUnknown;
  const vCai: _CoAuthInfo);
begin
with vCai do
CoSetProxyBlanket(Itf,dwAuthnSvc,dwAuthzSvc,pwidechar(pAuthIdentityData^.Domain),
    dwAuthnLevel,dwImpersonationLevel,pAuthIdentityData,dwCapabilities);
end;

procedure TSecDCOMConnection.SetPassword(const Value: wideString);
begin
  FPassword := Value;
end;

procedure TSecDCOMConnection.SetSvInfo(const Value: COSERVERINFO);
begin
  FSvInfo := Value;
end;

procedure TSecDCOMConnection.SetUser(const Value: wideString);
begin
  FUser := Value;
end;

procedure Register;
begin
  RegisterComponents('DataSnap', [TSecDComConnection]);
end;
end.


代码中有一些C风格的注释,是因为delphi没有为我们预定义这些变量和数据结构。
如何使用呢?将这个组件安装在IDE中,并将其放到你的现有代码的远程数据模块中去,将原有指向TDOCMConnection的数据集控件设置成这个新的TSecDCOMConnection控件。然后你可以在远程计算机中设置最严格的安全选项。但是要记住应该为你要使用的用户设置合适的权限:给予远程激活权限、给予远程访问权限。
4、到现在还没有谈到访问的问题。首先激活和访问并不是一回事。一个用户可能拥有激活权限但没有访问权限,也有可能只有访问权限却无激活权限。前面谈到CoCreateInstacnceEx可以用另一身份激活对象并取得IunKnown指针的一个本地引用。如果你直接用这个指针去取得IappServer接口并调用方法,那么你很可能又会见到“拒绝访问”信息。这是IUnKnown指针的本地引用存在于客户机的进程中,再没有做特殊设置前,该指针继承了客户机进程的本地令牌,也就是说当用这个指针获取远程IappServer接口时,会用客户机当前登录令牌调用QueryInterface,在调用过程中远程计算机将有此令牌中缓存的用户名和密码进行再次登录验证,当然此时又会被拒绝,而后远程计算机再次尝试用GUEST帐户登录并获取com对象接口,此时若没有找开GUEST访问权限,则客户端访问失败,windows返回“拒绝访问”信息。那么怎样才能使QueryInterface调用也使用远程用户身份呢,这就要调用CoSetProxyBlanket强制设置本地接口引用使用远程用户的令牌。在上面的代码中,我用MySetBlanket包装了该API,以便使用激活时的用户身份调用QueryInterface。而后在取得的IappServer接口上再次调用MySetBlanket,保证在使用该接口时也采用远程用户身份。
      MySetBlanket(mqi.Itf,Fcai);


      qr:=mqi.Itf.QueryInterface(idispatch,iiu);


      olecheck(qr);


      MySetBlanket(IUnknown(iiu),FCai);


为保证直接引用DataProvider的TclientDataSet也能按上述要求工作,在扩展的TSecDCOMConnection控件中,重载了GetServer方法。这样TSecDCOMConnection已能完全替换TDCOMConnection实现便利的com+应用编程了。
由于时间仓促,写这篇时很多术语没有做解释交待,因此可能会有一些不太好理解,但是出于为delphi Fans提供一个简单的实现安全性com访问的方法,我还是将这篇贴上来,主要是可以让需要的朋友直接复制代码用在自己的应用上。使用TSecDCOMConnection后,服务器方的com+对象可以强制找开访问检查,并打开组件级的访问检查。在打开访问检查的情况下,必须将服务器中允许访问com+对象的用户名加入到角色中才能正确访问。
(上述代码在delphi7/winXP sp2中调试通过,对于windows98和windows nt4.0及以下操作系统,由于CoCreateInstanceEx不能直接生成com+对象的安全上下文,因此代码不可用)



                昆仑踏月  于乌鲁木齐 2005.6.18

最后编辑5207 最后编辑于 2007-09-19 14:30:32

TOP

 
1/1页1 跳转到
发表新主题 回复该主题