非匿名方式访问远程的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:DWORD;
        dwAuthzSvc:DWORD;
        pwszServerPrincName:WideString;
        dwAuthnLevel:Dword;
        dwImpersonationLevel:dword;
        pAuthIdentityData:pCoAuthIdentity;
        dwCapabilities:DWORD;
    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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值