|
$ G) }* T! d8 | B( N. B" H# @
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 4 Y* G2 A' j a& o2 l
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
0 w1 C* d7 o; p3 l3 f6 h安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
! v8 U8 E8 R; _% x现就我以前对MIDAS编程的理解,做一个简要的介绍,
* Y; i6 y9 W" j1 n以后有时间精力再详细举例说明 ) [% T7 D/ f6 K5 n) V* T( V
3 @; s k- L2 |& Z7 S0 R, ^1 t
win2k中dcom的安全级别有7种,其中
3 d b+ p5 }/ y6 b* t8 v8 a最低的“无”安全检查暂且不论 ' i8 b6 g' K$ X' W. O: @! a& @
默认的“身份验证”服务方式几句话也说不清, 0 W0 F: r9 |0 W9 ? m( j) W
先放到一边,看看剩下的5种
. |2 U' n4 B5 W
l: L# _) g( o9 a! j* x' B1.连接,仅对初始连接进行安全检查 & T( i6 I0 }5 }. {; e! V
2 f! X- ]: j6 V5 Z 因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule + m5 u' W7 \5 _9 F, I" c% l! `
实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
4 @0 W" R% I- O2 v' a2 {! L' x* ] 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如
& h2 R7 V0 f6 H interface ILoginServer: IAppServer
2 @3 b7 [2 Q! I- m( h3 x8 R9 o M{ : o# J9 K) @5 q" u; K- p
[ 8 `1 T/ p# z3 `! P( _* Z; c- Y
id(0x00000001)
9 m3 F# O* D% f, _: s% y0 y, ^ ] ( ~1 z$ V4 @, V+ B
HRESULT _stdcall Login( void );
$ Q0 `8 k) D# v2 d [ ' S6 Z) L* W X& `( v9 {
id(0x00000002) ; R% \, x7 Q( m* T! f! M9 T' [6 o
]
6 @( H7 c: N& i% m* ^* y9 m HRESULT _stdcall Logout( void ); 2 e# z* [: h1 x/ R; M) |+ t; N" m6 a
};
: x% M: J# i0 d2 J 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider
C5 ~! L# t4 o, v) M, } 的Exported属性关掉即可让客户端无法看到服务器端的provider
. E U+ V p* W 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
8 W; e9 f0 k, j, @3 V8 t 所有的Provider,如IAppServer::AS_GetProviderNames可以取得
7 D$ U; g' H6 ] 服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
% c) V4 b; r2 ?! B8 w! O 都是通过相应的ProviderName来指定的,如 + |. ?' T j7 a9 o. o: ]
virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in
) ^0 m5 q( L% J% m: C9 Ot Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian 5 u, G, l" @/ C6 M
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; 7 \# b9 U5 s* v% V; }( s4 V. e
取得数据方法的第一个参数就是你需要操作的数据集名称……
2 W/ n$ s. _: Z# \6 h' k2 M 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, 1 r: v- H8 z6 ]+ [7 h2 E
则客户端看不到任何Provider,也无法通过其ProviderName取得数据
6 u8 B4 i6 ?/ K# o0 m6 l 例如我在服务器端的RDM类中加入
6 c2 H6 N* Z& }, F3 p0 ?class TLoginServer : public TCRemoteDataModule 0 x: o8 U& U( s& \8 X
{ 6 d) \9 Z# T, u2 S0 Z* V
...
! O) D4 o. }1 ^8 J" i8 Cprivate: // User declarations
7 h$ B1 s( b2 u ^* T8 ^+ ? o bool FLogin; 6 }$ f( [! F t, b& `2 T* Q
void __fastcall SetLogin(bool value); ! x5 q, O' k- @
bool __fastcall IsLogin(OleVariant &OwnerData);
2 e' B. y3 T) N9 w...
6 H1 z7 C$ ]' z/ ]6 [9 v__published:
/ x" l3 P, R9 |6 ?! I: @3 E& o __property bool Login = { read = FLogin, write = SetLogin };
+ ~" r$ W" v% `};
* {' ] c9 P4 `在RDM实现类中 ( ]4 g- ?) m+ y- U% y7 g0 o# e) i
class ATL_NO_VTABLE TLoginServerImpl: 。。。 8 f2 O* z% }' @1 Z8 |5 H, Y) _4 B( f
{ 9 y; b1 }; U1 F9 m1 P, [& y) I; U
// ILoginServer : r( }! N- @) ^
protected: 3 L4 P% F) z% F
STDMETHOD(Login()); ; X' E8 W2 Y: ]: {3 K: ^. R
STDMETHOD(Logout()); 8 a2 |1 M9 J* J Z4 s. x9 q
}; : k8 Y5 S2 y& S. p8 R- G
然后简单实现之
; j& {/ T7 x3 k' c" F5 U: o6 J__fastcall TLoginServer::TLoginServer(TComponent* Owner) & d5 C% k" X5 R x- W3 ^
: TCRemoteDataModule(Owner), FLogin(false) + @8 `) h2 N, v4 `6 _
{
8 m* d4 \( h6 U& x: I) G2 `} 3 O9 S# e: l, k# Z( h% X, }
//--------------------------------------------------------------------------- & w0 k# ^$ v9 E
void __fastcall TLoginServer::SetLogin(bool value) 0 l7 }1 w9 `" n
{ & u0 v( l+ A4 f/ S, v; }" e
if(FLogin != value)
9 ^# f* l) B8 B; ] {
8 z/ t% q) C. `7 x, l! t FLogin = value; 5 O/ Q3 D+ d7 X! m* u" s; V
dspAnimals->Exported = FLogin;
8 W _9 |8 a( |, o. q } 0 ~2 J& L$ q$ {& S! l, C4 Q
}
% L+ V/ {5 d2 x* g* T//---------------------------------------------------------------------------
$ _1 q; ?9 p4 U. ^6 I: f0 J& s2 T! uSTDMETHODIMP TLoginServerImpl: ogin() 2 q/ w5 z2 o4 L$ S3 M1 V5 ^
{
% |- L; Y0 H7 N3 X5 `$ Z b1 q m_DataModule->Login = true; % d3 P+ j2 v. [2 h; o
return S_OK; , ` z6 J/ [4 E1 P' \: u3 Z+ |. s- ]" Z
} / G+ X; {" A7 J/ s
//---------------------------------------------------------------------------
7 b7 O! U8 z/ J/ PSTDMETHODIMP TLoginServerImpl: ogout()
8 l, x6 q7 O: G- f{
1 S# i u1 O2 K! F8 L/ D& g. q m_DataModule->Login = false;
8 h% H/ X: D$ j \ X1 d return S_FALSE;
$ @$ B2 X$ a+ o+ z2 s6 K}
1 o* j, e8 ^1 q. G9 ~+ j# L1 L- h# M//--------------------------------------------------------------------------- * p* k5 d. Y+ f4 E3 v: s( O% \
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) 9 F9 j! R, T& v: b/ c$ f
{
) a! q U7 ^5 E9 I4 ], B. C return FLogin; 2 e0 M$ x8 b, N/ L$ M
}
8 ^% g. w/ _% j3 |5 heasy 这样除非你的客户端成功调用Login方法,否则他是无法使用 ) p5 W8 M3 C, S' _9 u+ I4 C
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
8 B* ^/ P% J+ p$ S; _ + t( i3 l! s% H
比如客户端可以用如下代码 8 |0 F! T/ w$ Z8 v5 I0 {# w9 s2 E0 B
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer);
6 g* z1 \& k o) G4 ]0 H8 _7 ? ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; ! D. [$ j y b) o
dispLoginServer.Login();
V `) h) \( w$ { DM->cdsAnimals->Open(); 1 c0 _ E! V$ v- ]
实现注册,并且打开相应数据集…… 9 n5 \( k, o+ ~ M
# r6 }) @$ O3 h' ^0 \因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 9 H! z8 g& s/ W+ o+ U' v
绝大多数程序应该足以…… |