|
C3 z4 }3 G' y; A! W
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
5 n7 m& V" b) V: S! r. D1 R% a在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
; p% T& @4 ^0 J& W: ^$ ]安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案 ; C% W; S7 F+ ~. O" @, {
现就我以前对MIDAS编程的理解,做一个简要的介绍,
$ t9 ?# C4 b1 G2 a以后有时间精力再详细举例说明 ( W- a% l4 K7 R: r( _
3 {' e- C- q2 G* g# f
win2k中dcom的安全级别有7种,其中
8 U1 t7 k) p2 e5 l% S8 N- l1 F最低的“无”安全检查暂且不论 # L: X& ]8 H' g2 t( V
默认的“身份验证”服务方式几句话也说不清,
* J i$ J' b+ H/ z先放到一边,看看剩下的5种 0 K- l8 k' L) p& m
3 k0 }( g, |# W
1.连接,仅对初始连接进行安全检查 + k. c: U5 N: F
* b* H: A! g6 W5 ]& F
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
. b. ?% y$ G. ?6 }0 T0 w( i+ m 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
3 a% i( E# O$ i3 `5 B 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如
$ O8 v! U5 Y6 s# B interface ILoginServer: IAppServer ' C+ r( A; c3 N2 Q4 C
{
. Y' U3 i1 B- i7 m+ k [ e# L! T. _' b% H+ i( p
id(0x00000001) ) p2 ]# @" p: C }, x5 k- Y0 X
] $ U" d. G% B! Q7 p+ a* j
HRESULT _stdcall Login( void ); 2 h' Z. S7 t: `2 ~
[
' G# k/ v; b5 z$ u+ N- W id(0x00000002)
5 j. v3 w A) q3 P" \& ? ]
$ R/ M+ b; W6 d HRESULT _stdcall Logout( void );
" W1 n/ e: q. } f6 M( ]0 f) d7 z* P};
' \! v5 E7 ?; E9 W( b% E& _ 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider 5 e" |+ o0 w4 h3 M% O# h. K
的Exported属性关掉即可让客户端无法看到服务器端的provider . i7 i; p& v; [- |0 X" \
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问 - D! T( s/ d( g. g6 ]
所有的Provider,如IAppServer::AS_GetProviderNames可以取得 ) Z0 v" x/ [& Y3 o6 O2 E
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
/ w4 q- I" L# {% k% o! Z' T' t 都是通过相应的ProviderName来指定的,如
* s2 _6 R0 e) G" U) |0 o8 cvirtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in 3 y# C8 V7 p4 ^/ w# f* D* i
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian ( `" {, b; H$ l: S. z
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; / d+ t1 R& z( L5 Y2 P. ^6 x) H" @
取得数据方法的第一个参数就是你需要操作的数据集名称…… 5 Q2 D& H) z4 w" h# ?# \4 Z
如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
& I5 n$ ^7 `5 r7 X' s( S 则客户端看不到任何Provider,也无法通过其ProviderName取得数据
8 Z. {# V- i6 Y* c4 r( S 例如我在服务器端的RDM类中加入
4 [, r8 W0 L oclass TLoginServer : public TCRemoteDataModule
( Y, \* H ~; U. h- t# R{
2 v4 Z. I, Z5 [6 F! R, r0 R... ; @ _% S# n+ M# P
private: // User declarations 4 u' j9 C, O) O
bool FLogin; # w2 ~- z0 x0 U' U& l, [, L6 T
void __fastcall SetLogin(bool value);
' s* Z. y% W) \" K4 e bool __fastcall IsLogin(OleVariant &OwnerData);
7 G0 Y( A2 L3 l# ?1 O3 Q1 e% N... ( A( Y. i7 p, ~0 s
__published:
Z/ [$ g5 {0 ~ __property bool Login = { read = FLogin, write = SetLogin };
0 `$ t* w w E6 H};
8 g. S. D) [* ^- I4 ]% {在RDM实现类中 : D: T! a# _1 A
class ATL_NO_VTABLE TLoginServerImpl: 。。。
- \( D6 a6 p) R! b{ ( S, \) q# t5 Q& I6 S# K( W2 l
// ILoginServer
# l1 |' j4 e* R' ]protected:
+ w6 Q8 j5 W. ? ~$ h7 t5 Y STDMETHOD(Login()); ) \, Y" J4 }9 r( U
STDMETHOD(Logout());
# ~, r5 `& V; ]3 V0 ~}; / \! i4 _5 q) F9 Z) L+ H" Q
然后简单实现之
# o2 |! F3 \ m# L__fastcall TLoginServer::TLoginServer(TComponent* Owner)
* `: [/ H& u* A4 X7 X7 d, v9 @ : TCRemoteDataModule(Owner), FLogin(false) / V! P, d2 f) ?# A! r f% G$ E; y
{ # ^- }( H% P& a- S0 T5 P3 G
} 9 L7 ~, C2 L" V- o0 x6 t
//---------------------------------------------------------------------------
; F. Z$ M# L3 {8 ~7 D/ evoid __fastcall TLoginServer::SetLogin(bool value)
4 a1 F( J' ?& F$ c Q8 Y; {! y- d{
% o! I; @' J5 k( `8 ` if(FLogin != value)
* b8 U L: g. R7 E5 n* P/ l { ! Y4 B1 F3 c! H5 n1 X" R' e; x
FLogin = value;
! W) A- T2 c' L( x. m dspAnimals->Exported = FLogin;
0 _3 ?+ E6 @& H& | }
+ J/ S" E* i! P4 l% n9 G}
( _# j# X+ F' d t4 V5 }! Y5 _) I//---------------------------------------------------------------------------
6 W1 f$ M* Q7 T8 SSTDMETHODIMP TLoginServerImpl: ogin()
- U* u2 h. {1 e, X7 p1 T{ 3 l3 ]7 q4 k8 @$ K9 e
m_DataModule->Login = true; 8 B) p' j7 h) E) P; I
return S_OK;
* } V- j3 ^" U) i$ r& W# u0 O' o5 v} 6 [, B. t% Z I8 u) A- u) F+ m
//--------------------------------------------------------------------------- 3 Q4 W8 X" c( R. P X: p: @
STDMETHODIMP TLoginServerImpl: ogout() ' e9 Q) i7 i/ P/ N/ y
{ , k0 f. Y& j' o) W8 ?6 C
m_DataModule->Login = false; 7 O; ^+ b6 X4 V ?2 M1 s
return S_FALSE;
. D. a4 n" Y2 R% f' J6 N} 1 R7 _5 h3 K7 c/ u
//--------------------------------------------------------------------------- + [% t0 p3 D* ]; \# A7 o
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) % |. x: ^/ B& Z' e% G4 |$ L' p
{ ) i. g5 p6 X% `) Z- u6 r, G
return FLogin;
7 |7 S9 I2 W* a C& x; {} 9 z F4 }5 W q; c
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用 2 V" {, v4 N1 Y+ E9 Z4 Z
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同 / c) r) o$ s$ V0 P9 b
5 A. @& v" S3 P1 h. u; K0 F8 X
比如客户端可以用如下代码 2 x5 w# L0 g* h) M D
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); {+ {/ k9 u. G" f; c9 I% [
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer;
8 E( p( r( p# z$ X# J Z: Q8 R, T6 m dispLoginServer.Login();
4 [) |' p ^+ b+ L3 ] DM->cdsAnimals->Open();
# W; |7 B K0 P6 R. t实现注册,并且打开相应数据集……
+ U# X! Y( ^% R' H3 b* I5 _
, y* o0 H9 ~# k* A因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 : r6 Y+ w1 P1 }5 j& J
绝大多数程序应该足以…… |