|
- ?1 q4 h$ u+ a" ?
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
; ]3 M# ~& s1 F6 _7 |1 Y/ T在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
* e: }, a& J$ \8 H9 }安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案 9 z' Z; J, G2 ~7 y
现就我以前对MIDAS编程的理解,做一个简要的介绍,
7 ?0 S3 A p n8 W3 Y以后有时间精力再详细举例说明 9 X! c$ t- h0 V& w' y6 j: c
* `+ F7 S! A# m) s4 J% ]
win2k中dcom的安全级别有7种,其中
1 s' A; g* z( g7 n最低的“无”安全检查暂且不论 + Y: F* G% S: S4 N1 f
默认的“身份验证”服务方式几句话也说不清,
& }& U( `( \2 c1 g: ?5 f" E先放到一边,看看剩下的5种 6 F" E y; b8 `) Z- l F
6 P) G& s4 r: q# T: l' }, v; @9 n1.连接,仅对初始连接进行安全检查 # [& [7 n. R# w+ ?& H2 |
6 W# S D% w) a L* [/ P8 I$ ` 因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule - z' y+ T. x4 W/ n& [9 L* W6 ~
实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
! @5 g, R$ j) c) `, }) D/ q$ H* l 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 + ]0 R& \2 L; u5 T6 O* Y5 [1 `
interface ILoginServer: IAppServer
+ M# m* e1 A2 a. O, A{
5 J# n, F& T, M, p [
/ n, e/ B1 z. w {& D id(0x00000001) 5 `5 N/ C2 a$ h; Z7 x+ I2 ]
]
; ?7 a' j- a6 E HRESULT _stdcall Login( void );
. {1 Y7 p2 S: T [ + E2 x" o8 z1 ]" H {5 C4 ?
id(0x00000002) 2 Q Q8 D( [5 j4 J+ g- e. c
]
* ~3 N5 X+ u* }* O: [ HRESULT _stdcall Logout( void ); , Z" {% q$ {0 K8 J8 ]1 H- s; ~
}; # h; y& y( H* w7 P
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider ; F4 x. _( J2 M7 z) B5 c& c
的Exported属性关掉即可让客户端无法看到服务器端的provider ' k w3 J% h. F: I" l1 T1 r
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
2 t5 D1 v2 B+ n 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 0 m+ c+ D \: w, i/ ^9 }
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
8 c6 i. l( I9 c k" m( V 都是通过相应的ProviderName来指定的,如
# a0 R7 Z0 K1 g L; Ivirtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in 5 {9 ?9 Z5 k/ M
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian 6 z% w9 ?# P% l& i5 m
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; # j5 a* i& E% e( B0 A5 j
取得数据方法的第一个参数就是你需要操作的数据集名称……
/ w% J# q; t/ g8 ^ 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, ]" r, n( E5 ~: W9 W
则客户端看不到任何Provider,也无法通过其ProviderName取得数据
' T( t* q# ^: \0 u( E; y, u 例如我在服务器端的RDM类中加入
- X6 h* M5 M4 y( ^) w3 Uclass TLoginServer : public TCRemoteDataModule - w9 l9 n: d1 J, i& f, _
{ + Q: B+ w ^1 `, I) K# ^
... * F% {, i4 D) M' y7 M
private: // User declarations
& _5 f6 ] l2 v bool FLogin;
& [1 Y0 P' [1 t, o4 w1 r- O void __fastcall SetLogin(bool value);
5 a7 P, l% s( ]3 ], U; B; n4 f bool __fastcall IsLogin(OleVariant &OwnerData); ' v2 V d- j$ t+ `& h) f6 O8 W- t
... 2 r/ D% w, b' r; S
__published:
$ U! R# V r& _ __property bool Login = { read = FLogin, write = SetLogin }; , }1 L- @! d% x9 z' \
};
, G! a7 o$ }" |, A8 k( S/ v在RDM实现类中
3 I( f0 A1 n$ M5 D `class ATL_NO_VTABLE TLoginServerImpl: 。。。 8 [- i+ X: F' W% n8 D* g
{ 8 }* a( v9 g7 F5 K: [
// ILoginServer - o ], R' u! B& X
protected: 1 U- P( b8 p( t/ V
STDMETHOD(Login()); 5 g$ Z; C+ j+ X( D9 u
STDMETHOD(Logout()); ! p# h& {; q* i( Z+ J5 e0 Q
}; ! e. }# M3 P, {' F( |
然后简单实现之 # x i- q) Q' M$ h: O# @! h
__fastcall TLoginServer::TLoginServer(TComponent* Owner) 3 z# B- d, `5 ~1 f& _+ }
: TCRemoteDataModule(Owner), FLogin(false)
4 i o! \# U- Q+ N$ v{ # n4 @9 q9 K0 |2 h; L
}
4 {- |1 \" v3 t( ~$ v//--------------------------------------------------------------------------- 2 U8 {% A# ?( D5 s$ s, I
void __fastcall TLoginServer::SetLogin(bool value) # A7 H3 }6 Z7 o, A
{ R1 e3 J$ G+ `
if(FLogin != value) " L g$ {8 Q0 W& r& N+ h% y
{ 5 @6 M% K- c! m9 W. p9 H' b$ P
FLogin = value; 5 V/ i. p6 ^$ o `
dspAnimals->Exported = FLogin; / m6 P8 o# Q; {6 d/ q& K1 ?
}
3 k6 F' A0 F4 d}
/ A! f% Z4 S E7 `//---------------------------------------------------------------------------
5 m& v+ H9 d' j* ]! ^0 _& WSTDMETHODIMP TLoginServerImpl: ogin()
) {2 [, H0 l. W1 P6 \: S{
$ R/ {+ a! o) f" n7 e7 @ m_DataModule->Login = true; * ^( B( w+ ? u
return S_OK; ! h- ~. w8 t2 N$ [+ T# R; P
}
% b: a# i& H* T( U3 @0 O( v//--------------------------------------------------------------------------- $ \# [, D( ^' ^
STDMETHODIMP TLoginServerImpl: ogout() ; X* q* `5 H" s4 X
{
* w' \! U3 Y5 {9 ?) I# [% y m_DataModule->Login = false; ! }9 Y3 `) H d# U$ f) x7 ]
return S_FALSE; : T$ H; g* o0 I, y5 t
}
( {6 n! n# }% _6 N2 A//--------------------------------------------------------------------------- 9 P7 |3 P8 e8 ?* n+ q0 X
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) 9 P, \2 J* F8 L& d, x
{ 1 a" J" e! K+ m2 k
return FLogin; 6 S4 ~3 m) h; F4 P
}
" ]) D; h) `- Q; r6 }: s6 q' ]easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
, P* Y3 {1 ^# D# w1 g) LdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
; @: u& L! Z) M0 x
+ [# U$ X0 x6 u' C% A# W+ |+ s比如客户端可以用如下代码
* {) U) N; A1 F6 D+ o2 z$ L; d IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); 1 b; D3 t" A- `) b$ y
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer;
* ^6 \" |( @2 h( y' ^ ]3 z N dispLoginServer.Login();
+ f7 c6 {# n+ K h! V DM->cdsAnimals->Open();
, {; |! o* s! _实现注册,并且打开相应数据集…… 2 W( s5 {- t1 ?: P
7 j0 O6 p7 x, F6 Y3 V# [因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
( O% S( {/ s( K4 @+ C) ^3 q绝大多数程序应该足以…… |