|
6 H6 L6 T0 a: y/ v$ G( u$ i前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
: w6 d" F/ n) W: Y+ V) h在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
+ N+ Y7 t7 E& N n+ U安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
% Q& [, G9 R7 i6 G) `: v* t现就我以前对MIDAS编程的理解,做一个简要的介绍, % L9 X2 N0 i6 o! n% M0 a2 C7 T
以后有时间精力再详细举例说明 / P0 T: U8 m9 S' y
+ x' c3 \% Q% f/ Z
win2k中dcom的安全级别有7种,其中
, V/ u/ `! Q! O/ r最低的“无”安全检查暂且不论
0 F$ s0 Q2 K/ \( I/ Z4 B默认的“身份验证”服务方式几句话也说不清, 3 L% u! r- u& ?# @3 |; B
先放到一边,看看剩下的5种
0 Y% M7 k! W( W; ` ; W& H$ u; v* `" z% n
1.连接,仅对初始连接进行安全检查
# _3 u* t2 p+ A4 e( U" C1 J 3 M2 ]8 z1 r% Z
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
" ~" o8 v+ J# A0 Y- G; D 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的 5 _ D! r1 _6 d! Z! c7 K7 [
可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如
$ J$ F3 p' u5 J5 V/ } interface ILoginServer: IAppServer 8 e, G5 S8 J1 t' B9 `' ]% s9 R9 Y
{
& {6 ^; i/ V8 R) H) `' N [ , u& \" q K: V4 d0 |7 g, h3 e
id(0x00000001)
9 w) \/ {3 f+ U+ s [0 D ] : y! A1 E: ?0 B5 D/ K( L9 M, U
HRESULT _stdcall Login( void );
e: G8 f3 y) q- T% ^ [
0 Y4 Y: {1 t! ] id(0x00000002)
& m5 m/ m# G: \0 }1 u8 S ]
, Z0 d" A. w1 ?# d9 I, ?) O HRESULT _stdcall Logout( void ); @6 U8 U3 H, E, t
};
$ D% C1 d4 Y5 i 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider ( m! O, e, q( r! M; T( A
的Exported属性关掉即可让客户端无法看到服务器端的provider
" Q0 G6 c2 G" q9 U8 b* _8 D 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
/ S% E+ A: C2 S. T, S3 V 所有的Provider,如IAppServer::AS_GetProviderNames可以取得
0 {9 }! ? Y: |; L0 K4 r5 ^/ K0 i 服务器端所有exported的IProvider接口名称,而所有数据的取得、修改 4 x4 f; N$ ~+ l. l! }0 e) Q' r
都是通过相应的ProviderName来指定的,如
9 x, A7 _/ G0 \+ W2 v5 ~# Z ^virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in ; u' k% _# f) s- Y0 [
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
6 i8 @+ \ t8 O' A* V& F* ?t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; ) ^ W: U: X% a! M- b/ u/ U
取得数据方法的第一个参数就是你需要操作的数据集名称…… 2 A; u+ F$ D" t& e9 U* Q8 h
如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, 3 \4 y) x, R) a* r; T
则客户端看不到任何Provider,也无法通过其ProviderName取得数据 " k! S( P7 s, n4 ^
例如我在服务器端的RDM类中加入
: m+ |; W' M+ @" d" O6 ^8 Pclass TLoginServer : public TCRemoteDataModule
& W$ A! S: b+ m4 b5 U7 ]{
% i; h+ D' p0 O2 _$ P...
8 y% ~ `; @0 [) m B2 qprivate: // User declarations # @) f7 \/ L! b' X5 b
bool FLogin;
5 N) i. n' S: x( D void __fastcall SetLogin(bool value);
. o- I! f$ l. A! q5 P( I bool __fastcall IsLogin(OleVariant &OwnerData); " z8 a/ r1 l1 j# a7 g8 H4 ~
...
o! U$ D! D% m( c__published: - e' I" m% U- E+ P
__property bool Login = { read = FLogin, write = SetLogin }; 9 N% \0 a( q! j6 _* r& P+ x
}; 6 e" S& e3 ]% g( ~/ u8 z
在RDM实现类中
6 [- r4 o1 M: U7 E# z. c" ?& \9 Pclass ATL_NO_VTABLE TLoginServerImpl: 。。。
+ D+ T, l9 s& C1 P{
- F6 `" u+ M4 v1 z// ILoginServer , D' J' i f) z
protected: 7 I* F# t6 J; V* M$ u
STDMETHOD(Login()); + m5 |1 u5 C1 v5 t" Q/ i, U2 w2 o
STDMETHOD(Logout()); 2 g: v* ~. ^+ T% S' k* T
}; 2 b% b- ~! m) F# l
然后简单实现之 3 L6 N# H8 _1 u" q% S3 B# E# X
__fastcall TLoginServer::TLoginServer(TComponent* Owner) + X5 d& l' e8 p( ?
: TCRemoteDataModule(Owner), FLogin(false)
) F( `9 N4 L: ?$ }{ 6 ?$ v4 n" f h! O
}
1 y& \% m/ n" Y8 a8 `4 [//--------------------------------------------------------------------------- & v; y, e: L; e9 v7 B7 o: {8 Q
void __fastcall TLoginServer::SetLogin(bool value)
W$ g# c& n7 x9 \% U1 D# C{
" Z/ |4 z+ J2 m) r! H* r if(FLogin != value)
% P2 Z. v; Y9 a { 2 n B) @+ x* B, G# P* V" T& J
FLogin = value;
' R* l9 M6 R% }( o dspAnimals->Exported = FLogin; . ]9 h- N8 S& E/ z. a
}
& ^3 \, \$ z. W} * l( w3 z1 T* F7 @/ b V
//--------------------------------------------------------------------------- , f V/ }) {1 Z8 n4 e* I, f6 z" g
STDMETHODIMP TLoginServerImpl: ogin() + {4 |' |/ E& w
{ / y. `# H+ B; S7 M( m
m_DataModule->Login = true; : \7 j# s3 C( c- W$ _0 H' { c( G1 \
return S_OK;
0 R8 A2 {' Q1 Y. E}
! R6 I, v5 L; \3 z( y8 l//---------------------------------------------------------------------------
$ o0 ], b+ G" g. g3 JSTDMETHODIMP TLoginServerImpl: ogout() . O9 Q& k7 r, `& |& m4 W6 V% ?
{ : w3 K/ f* ]- y5 p5 K: o5 W! n) H) c' K
m_DataModule->Login = false;
9 S3 K- q9 v* c4 l! O3 ~ return S_FALSE; 5 ]4 M0 I1 C9 n; S( B' J
}
c, N4 f+ J5 ], V//---------------------------------------------------------------------------
+ W$ W5 l( X$ v! A zbool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) 2 Y& s" R. U) k8 I& x1 P
{ & t8 @6 \8 [* O2 ~' m
return FLogin;
; p9 I$ I9 G0 Q# ]3 \% j; b) v6 n+ m} 9 a/ V& Y5 [* r; S. C7 b J" g
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
5 L: B6 U3 }5 G" AdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
6 y. V$ v& I$ o8 ^ G/ Z/ ` 4 @- J3 e, v: Q/ P
比如客户端可以用如下代码
6 D- t# F. T+ I( ? IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); 6 y }+ X' u5 r+ f) [9 V8 c
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer;
/ E+ g* J" a x dispLoginServer.Login();
3 j4 ]: P# M/ M8 h' H/ A% R DM->cdsAnimals->Open(); * j u. }' G1 } q
实现注册,并且打开相应数据集……
& g: }; C6 [/ J* E$ ?
" X. d9 R& b0 R0 v因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
5 D8 {1 U) {% D绝大多数程序应该足以…… |