|
0 q) k& W3 c5 w" Y9 Q
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 ( X) t' x0 i2 h/ T6 \
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
3 `5 Q8 G4 S! F& c) G安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
: w; [8 H# I1 }' i8 p% m现就我以前对MIDAS编程的理解,做一个简要的介绍,
& ^% U; S, o1 e& Z以后有时间精力再详细举例说明 u5 F4 w7 C6 j0 s4 c
8 n. M3 _( D) p4 r
win2k中dcom的安全级别有7种,其中 : y9 e; F+ ]- ?( F. L
最低的“无”安全检查暂且不论
) e( X' O7 M1 b3 W% Q, e4 N* ]默认的“身份验证”服务方式几句话也说不清, ( [* j* U! |1 s: L/ `5 C( d3 D
先放到一边,看看剩下的5种 ( U) T$ d1 D2 S6 x* e' s
: Y( ` w/ ?( X
1.连接,仅对初始连接进行安全检查 ; S l* d U) W" X5 X
* a5 W, ^6 P. T0 A2 @/ K
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule 3 d2 ?2 H# t( x
实例检测一次即可,实现方法很多,这里简要介绍一种最简单的 , ^* g' J' F& q: Q) ]
可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 # x0 D, ?/ V% O" w, k1 v
interface ILoginServer: IAppServer
t5 |1 q7 J* B. o{ 6 s! B6 T, |0 m( }# ^1 N' n8 m
[
4 j$ v7 a* e/ t id(0x00000001) & U* ~& D# T7 \
]
. H8 Q# L* r Q8 d7 J HRESULT _stdcall Login( void ); 8 ]2 v/ S# T2 _$ s5 g" ]
[
r) H3 D* \" z. e- k u) C2 g id(0x00000002)
* E& x! [# |, v- v$ t ]
( z# p5 l7 D: ]4 N/ b& k$ ^ HRESULT _stdcall Logout( void );
) \; M) J, {7 F" O, `! |( W' \};
% m; t" c. G' V* i) p0 p# l 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider 4 d) n* }. v- A3 t( m
的Exported属性关掉即可让客户端无法看到服务器端的provider % c I3 @9 r* L9 V% b! `+ e
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问 & S7 T! @- e+ j" I( M. \0 o
所有的Provider,如IAppServer::AS_GetProviderNames可以取得 ; a9 y# p9 @) F1 r p. t
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改 6 \" O9 U1 d# r) ?7 u
都是通过相应的ProviderName来指定的,如
& r& `4 F) s+ F* h3 I% Q4 F) C( Svirtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in
5 e' E0 |! T, M( u& y# Xt Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
: i* C4 u; x. Ct & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; 6 M" o9 z4 W Y/ E; I. Y" C
取得数据方法的第一个参数就是你需要操作的数据集名称……
, R8 C" e2 x* |: `0 V 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
" P1 `! }! ]$ Q 则客户端看不到任何Provider,也无法通过其ProviderName取得数据 + V; B' l* `/ e2 J& W- O7 ~
例如我在服务器端的RDM类中加入 % K# X8 D3 R' \- G& o# }; l1 L
class TLoginServer : public TCRemoteDataModule
7 G: ^% Q* l1 J- q. q{
) y) e& u2 O3 D8 y, o; A' h1 G2 a... * f: M1 I. z/ U1 C$ e
private: // User declarations - r4 C! s" a, `
bool FLogin; ! T( ^; L- r+ O
void __fastcall SetLogin(bool value);
E J; q3 i" N( m4 h bool __fastcall IsLogin(OleVariant &OwnerData);
* p' L, E# p' n1 l+ b/ H... ; H8 J) n# U6 B+ q
__published:
; m4 U U% K# ~ __property bool Login = { read = FLogin, write = SetLogin }; 9 L- Z6 i, \! j" c( A$ ?6 C
};
" `7 B: t) i5 H# d在RDM实现类中
9 {/ r; [9 ~' U/ J- o4 d" rclass ATL_NO_VTABLE TLoginServerImpl: 。。。
' V; ]6 h; m3 U{ / ~ r7 o! R% J) _) h, u2 h% A U) c
// ILoginServer
" v( w* o( S1 `3 Z2 e# Tprotected:
+ C* E$ B6 b# T STDMETHOD(Login());
) O+ E# C/ Q& v; b: j6 r2 \: j STDMETHOD(Logout()); * R5 N! k' {- k2 X7 _0 U$ v
}; D' o, D$ X, Q9 U/ B+ f( v
然后简单实现之
* t4 }5 P& w: |; x: C* ?__fastcall TLoginServer::TLoginServer(TComponent* Owner)
: r/ n s$ ?2 W$ I) ^) ]9 M : TCRemoteDataModule(Owner), FLogin(false) ' v; J( v& N; _4 \$ q0 X) c
{ & ^& A7 ~ ~4 D6 Z/ p1 U
}
9 a. Q& j! ^2 ]- p+ {6 a4 \2 ~+ z//--------------------------------------------------------------------------- ; _1 |% o v/ E+ ]7 s2 n5 d. ?
void __fastcall TLoginServer::SetLogin(bool value)
/ u9 r0 e; w3 `) t7 l9 Q{
# F8 E& P, H& l# g: U8 v* u- i! p, | if(FLogin != value)
* C) k9 B _0 W7 A* Q4 G# ]6 L) p { , w( \) Z3 }1 B1 d! l3 B) C
FLogin = value;
' q o4 y7 i- J, i- ?' V dspAnimals->Exported = FLogin; ) h8 `; |4 ^% v( I% d: n
}
, q; c/ R$ w7 f. k& ?1 V3 Y5 |} 8 y, P/ b' f1 \$ a
//--------------------------------------------------------------------------- # M3 `: j4 ^, t' f9 y2 U
STDMETHODIMP TLoginServerImpl: ogin() 0 [% i1 N1 ^# u& t6 z y
{
7 R+ @" }* G0 b8 y6 Y0 P m_DataModule->Login = true;
* k: \* }* {2 ^1 P! K0 n return S_OK;
3 }* V; D! x9 Z7 l& D4 W} 2 M& ?9 n. E" h5 F
//---------------------------------------------------------------------------
& [* q9 h8 z4 t e: O) mSTDMETHODIMP TLoginServerImpl: ogout() 8 j& u! ^+ T# F0 t8 P
{ c3 z8 a; X$ V5 Q
m_DataModule->Login = false; ( O! B8 g6 W: @4 |: e
return S_FALSE; , W9 U) ], R- v% Q2 g, P
} 1 g4 j* T7 ~5 y7 n3 l
//---------------------------------------------------------------------------
7 F. v4 d. X, Tbool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) 6 x, R. m) L, @2 e ~
{ 4 Y' e( p+ R, [) Z
return FLogin; ! j2 j* V' @$ L& H' d+ e
} 2 C6 n/ V( [* e% u$ ~) i s
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
7 i; e2 j$ Y* j" l, zdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
' i8 Q& V- T8 K8 p
/ v9 w4 B+ P2 v比如客户端可以用如下代码
' R2 l3 x3 j, y- e' } IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); ( g6 X/ n4 i. l4 B' O. h: ]
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; ' @0 B, x. V/ t' v; G& W
dispLoginServer.Login(); 6 F$ g, I2 V; m. Y
DM->cdsAnimals->Open(); 4 [9 Z3 \0 [0 a
实现注册,并且打开相应数据集……
8 Y3 f( w0 l" U9 c: ~/ ? 3 ?9 y- l- |3 G
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
8 C. K6 }$ }3 A/ f绝大多数程序应该足以…… |