国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

以遠程計算機上的用戶身份訪問Com+應(yīng)用

2019-11-18 17:59:31
字體:
供稿:網(wǎng)友

以遠程計算機上的用戶身份訪問Com+應(yīng)用

DELPHI程序員開發(fā)com+應(yīng)用的速度是非??斓?,其主要原因是其較好地封裝了com+的windows底層功能,開發(fā)人員通過較為簡單的類繼承就避開了復(fù)雜的com+底層技術(shù)細節(jié),使開發(fā)人員將精力放在應(yīng)用本身的功能上面。Delphi在封裝com+應(yīng)用時采取了許多折衷,在保留通用性的同時也避開了一些實現(xiàn)起來困難但是應(yīng)用面不太廣的com+底層特性。這些避開的特性中最令delphi com開發(fā)人員關(guān)心的就是安全特性。從delphi 5開始,有許多人都面臨過這樣的問題:com應(yīng)用開發(fā)出來并且在本機上運行一切正常,但是一旦分發(fā)出去實施遠程訪問時,就無法正常運行了。我自己有段時間在看到“拒絕訪問”錯誤提示時會本能的頭暈。其實認真追究起來,還是因為自己對windows安全技術(shù)了解不多造成的。多年來我一直沒有發(fā)現(xiàn)國內(nèi)有windows安全方面比較系統(tǒng)的資料和書籍,直到Keith Brownr的<windows安全性編程>中文版的出現(xiàn)。正是基于這本書我才有了下面的一些試驗,也知道了為什么我老是補拒絕的原因。下面的討論只是我在解決自身現(xiàn)有代碼的安全訪問問題時,總結(jié)出的幾個小經(jīng)難方法。建議愿意了解windows安全性的朋友去看一看<windows安全性編程>一書,你會發(fā)現(xiàn)windows的安全不再神秘。
這篇文章將會說明如何以遠程工作站上的用戶身份激活com+對象,并以此用戶身份訪問Interface。
1、        Delphi默認com+對象的遠程激活

Delph中遠程com+對象激活一般通過TdispatchConnection及其子類來實現(xiàn),實際代碼中多用TDCOMConnection或TsocketConnectoion這兩個組件,TDCOMConnection組件最終調(diào)用CoCreateInstanceEx創(chuàng)建com+對象。CoCreateInstanceEx (const clsid: TCLSID; unkOuter: IUnknown; dwClsCtx: Longint; ServerInfo:PCoServerInfo;dwCount: Longint; rgmqResults: PMultiQIArray): HResult。

TDCOMConnection在調(diào)用CoCreateInstanceEx時為pCoServerInfo參數(shù)中的pAuthInfo傳遞了Null值,因此TdcomConnection在創(chuàng)建Com對象時使用的是本地計算機登錄者的用戶令牌。假若A計算機上的登錄用戶Auser使用TDCOMConnection類連接遠程計算機B上的com+對象,則B計算機會使用Auser的用戶名/密碼在B計算機上建立登錄會話并最終創(chuàng)建com+對象。但是一臺windows工作站上的本地用戶只能在本地登錄而無法在別的計算機上登錄,因此A計算機上的Auser就無法在B工作站上建立登錄會話,當(dāng)然也就無法創(chuàng)建com+對象,此時遠程工作站B會嘗試用Guest帳戶建立會話并使用該賬戶激活com+對象。在這種情況下,如果B工作站上的Guest賬戶沒有啟用或Guest沒有激活com+對象的權(quán)限,你就會看見令人頭暈的提示“拒絕訪問”??吹竭@里你是不對現(xiàn)在網(wǎng)上最“流行”的dcom配置方法有所悟了呢。那個方法就是允許everyone訪問、激活com對象、并且將“默認身份驗證級別”設(shè)置成無。這種方法能夠使你的com應(yīng)用可以“用了”,但是,它可以上“任何人”訪問。而且這種設(shè)置你將無法利用com+基于角色的安全訪問控制功能。
   2、怎樣不用GUEST賬戶激活
這個問題的實際上應(yīng)該是:怎樣用遠程工作站上的用戶激活遠程com對象。解決這個問題其實很簡單:只要你在調(diào)用CoCreateInstanceEx時為它指定遠程工作站上的用戶名和密碼,只要用戶名/密碼通過遠程計算機的驗證,并且該用戶被授予了“遠程激活”com+對象的權(quán)限,那么遠程工作站會用該用戶身份激活com+對象??匆幌麓a:

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結(jié)構(gòu)

  cai.dwAuthnSvc:=10;//winNt默認的鑒證服務(wù)

  cai.dwAuthzSvc:=0;

  cai.pwszServerPRincName:=wDomain;

  cai.dwAuthnLevel:=0;

  cai.dwImpersonationLevel:=3;//必須設(shè)置成模擬

  cai.pAuthIdentityData:=@cid;

  cai.dwCapabilities:=$0800;

  //以上填充_CoAuthInfo結(jié)構(gòu)

  FillChar(csi, sizeof(csi), 0);

  csi.dwReserved1:=0;

  csi.pwszName:=pwidechar(wdomain);

  csi.pAuthInfo:=@cai;

  //以上填充COSERVERINFO結(jié)構(gòu)

  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));

這段代碼中除了最后實際調(diào)用CoCreateInstanceEx外,前面的代碼都是設(shè)置參數(shù)。這些參數(shù)的含義請大家參考msdn,除了用戶名、主機名、密碼外,只有一個重要要部分要說明:cai.dwImpersonationLevel必須設(shè)置成允許模擬(值為3),否則遠程計算機將無法按提供的用戶/密碼建議網(wǎng)絡(luò)會話。
3、不修改現(xiàn)有代碼,可以實現(xiàn)用遠程用戶身份激活嗎?
當(dāng)然可以,我擴展了TDcomConnection類,為其加入了用戶名和密碼,并修改其默認的DoConnect方法,使其在調(diào)用CoCreateInstanceEx時用指定的用戶名和密碼填充參數(shù)。代碼如下:

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

    dwAuthnSvc:DWORD;

    dwAuthzSvc:DWORD;

    pwszServerPrincName:WideString;

    dwAuthnLevel:Dword;

    dwImpersonationLevel:dword;

    pAuthIdentityData:pCoAuthIdentity;

    dwCapabilities:DWORD;

  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風(fēng)格的注釋,是因為delphi沒有為我們預(yù)定義這些變量和數(shù)據(jù)結(jié)構(gòu)。
如何使用呢?將這個組件安裝在IDE中,并將其放到你的現(xiàn)有代碼的遠程數(shù)據(jù)模塊中去,將原有指向TDOCMConnection的數(shù)據(jù)集控件設(shè)置成這個新的TSecDCOMConnection控件。然后你可以在遠程計算機中設(shè)置最嚴格的安全選項。但是要記住應(yīng)該為你要使用的用戶設(shè)置合適的權(quán)限:給予遠程激活權(quán)限、給予遠程訪問權(quán)限。
4、到現(xiàn)在還沒有談到訪問的問題。首先激活和訪問并不是一回事。一個用戶可能擁有激活權(quán)限但沒有訪問權(quán)限,也有可能只有訪問權(quán)限卻無激活權(quán)限。前面談到CoCreateInstacnceEx可以用另一身份激活對象并取得IunKnown指針的一個本地引用。如果你直接用這個指針去取得IappServer接口并調(diào)用方法,那么你很可能又會見到“拒絕訪問”信息。這是IUnKnown指針的本地引用存在于客戶機的進程中,再沒有做特殊設(shè)置前,該指針繼承了客戶機進程的本地令牌,也就是說當(dāng)用這個指針獲取遠程IappServer接口時,會用客戶機當(dāng)前登錄令牌調(diào)用QueryInterface,在調(diào)用過程中遠程計算機將有此令牌中緩存的用戶名和密碼進行再次登錄驗證,當(dāng)然此時又會被拒絕,而后遠程計算機再次嘗試用GUEST帳戶登錄并獲取com對象接口,此時若沒有找開GUEST訪問權(quán)限,則客戶端訪問失敗,windows返回“拒絕訪問”信息。那么怎樣才能使QueryInterface調(diào)用也使用遠程用戶身份呢,這就要調(diào)用CoSetProxyBlanket強制設(shè)置本地接口引用使用遠程用戶的令牌。在上面的代碼中,我用MySetBlanket包裝了該API,以便使用激活時的用戶身份調(diào)用QueryInterface。而后在取得的IappServer接口上再次調(diào)用MySetBlanket,保證在使用該接口時也采用遠程用戶身份。

      MySetBlanket(mqi.Itf,Fcai);

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

      olecheck(qr);

      MySetBlanket(IUnknown(iiu),FCai);

為保證直接引用DataProvider的TclientDataSet也能按上述要求工作,在擴展的TSecDCOMConnection控件中,重載了GetServer方法。這樣TSecDCOMConnection已能完全替換TDCOMConnection實現(xiàn)便利的com+應(yīng)用編程了。
由于時間倉促,寫這篇時很多術(shù)語沒有做解釋交待,因此可能會有一些不太好理解,但是出于為delphi Fans提供一個簡單的實現(xiàn)安全性com訪問的方法,我還是將這篇貼上來,主要是可以讓需要的朋友直接復(fù)制代碼用在自己的應(yīng)用上。使用TSecDCOMConnection后,服務(wù)器方的com+對象可以強制找開訪問檢查,并打開組件級的訪問檢查。在打開訪問檢查的情況下,必須將服務(wù)器中允許訪問com+對象的用戶名加入到角色中才能正確訪問。
(上述代碼在delphi7/winXP sp2中調(diào)試通過,對于windows98和windows nt4.0及以下操作系統(tǒng),由于CoCreateInstanceEx不能直接生成com+對象的安全上下文,因此代碼不可用)

                 昆侖踏月  于烏魯木齊 2005.6.18

上一篇:ReportBuilder設(shè)置打印頁范圍技巧

下一篇:如何通過程序?qū)崿F(xiàn)網(wǎng)卡的啟用和禁止功能?

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
學(xué)習(xí)交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網(wǎng)友關(guān)注

主站蜘蛛池模板: 中宁县| 天长市| 理塘县| 灵石县| 保亭| 邳州市| 盐源县| 博爱县| 安岳县| 长葛市| 大石桥市| 教育| 宝丰县| 青神县| 永胜县| 射洪县| 武清区| 梅河口市| 河津市| 广河县| 泽库县| 永州市| 高要市| 呼和浩特市| 扶沟县| 江陵县| 西乡县| 霞浦县| 清河县| 离岛区| 惠安县| 西和县| 张家口市| 阿荣旗| 收藏| 囊谦县| 庆云县| 城固县| 泗水县| 泰和县| 策勒县|