|
- y% u% Y. d' b前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
& D8 s3 k. R# x3 x" b' a* w6 c! h在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 ( ~: n$ n6 W! x ~4 ]( x
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
% C: ?6 \& y* v& {( V5 D2 @" t! _现就我以前对MIDAS编程的理解,做一个简要的介绍, . E/ Z) g3 I% g6 g
以后有时间精力再详细举例说明 8 B: X! l) w& g3 S X
- f1 u' S7 H& z
win2k中dcom的安全级别有7种,其中 . k3 I5 k/ g9 y4 M
最低的“无”安全检查暂且不论 6 `% g/ e5 ^6 [& z& L
默认的“身份验证”服务方式几句话也说不清, + h x8 }$ ^* i" A4 I) n, Y( e7 ?: J
先放到一边,看看剩下的5种 7 s* S7 x' l: I% s1 x3 S6 a! p* T
; S9 j9 q1 Q5 }8 W# d9 P
1.连接,仅对初始连接进行安全检查
9 F2 {4 O1 {" B, _, k
+ }6 I5 I6 `% g# T 因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
: x4 N9 ~1 O$ r% H4 L! [ 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的 % t9 y: A( T* {! I8 I, ~: _
可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如
$ ^" t9 E' ^8 s' z7 c interface ILoginServer: IAppServer
( a( @; X- O4 k6 a0 h6 T" q{
8 z. g$ @- S m6 y% P s [
* w- r* o1 {3 @& N" s# W7 | id(0x00000001)
4 V" M( ^' F) k& z: D% S ] - V' f5 G2 `0 F# ?, e; U
HRESULT _stdcall Login( void );
. }2 ?/ o; j0 [1 ~# U% c6 R [ 0 @& |9 E6 a" ~( |3 z
id(0x00000002)
* {2 Q. l. J O+ t. d. C @6 t ]
& y$ @, y1 M8 {& t( n. V! j HRESULT _stdcall Logout( void ); / W# D: \. U% p1 S4 b
}; , c) q- h. x* n% t5 I* F% |; Y+ G
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider
. q9 \% u( X# m: G" S6 A! [6 a& \ 的Exported属性关掉即可让客户端无法看到服务器端的provider , V' ~6 I2 z( M- ^5 P
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问 - f8 o; M# a' U
所有的Provider,如IAppServer::AS_GetProviderNames可以取得
2 z* ~4 E% I: C2 L6 M2 J, O 服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
3 o* `: W. m" t3 E u, K# i: J' c 都是通过相应的ProviderName来指定的,如
) a; R4 u0 Z8 V, ] b, w% Jvirtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in 3 V, p' e) j& D" }9 Z
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian 0 n, `* i4 \3 F: X) E
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ;
2 {$ O3 |" R# M4 t0 J3 @; b0 q 取得数据方法的第一个参数就是你需要操作的数据集名称……
0 u: B7 o! j2 b: e7 j 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
7 g, O, S$ a5 z- g' H 则客户端看不到任何Provider,也无法通过其ProviderName取得数据
* k1 I& _/ y0 B- i; @ 例如我在服务器端的RDM类中加入
; |0 M2 ~& n7 w; U k6 {) {class TLoginServer : public TCRemoteDataModule , `3 H/ [5 _+ v7 }, J
{
* e; A& Z. J. l, f: H...
+ k# ]7 ?4 n7 F5 Q9 lprivate: // User declarations ! v W' {" l/ H/ j9 v0 V! J Z- V
bool FLogin; $ f2 d9 u, W+ V5 ]! c
void __fastcall SetLogin(bool value);
: V% f2 {% c/ p4 X1 P+ ~) q bool __fastcall IsLogin(OleVariant &OwnerData); " X9 R5 t7 |( f. _
...
/ }7 i( t& G$ }- V' e2 N4 `3 ~8 l8 R__published: ! B4 M/ _; t; a- U. m" ^
__property bool Login = { read = FLogin, write = SetLogin };
+ R% H C+ ?7 G, \4 I+ f}; $ [% C6 N& J$ `- P4 Q! k$ Q
在RDM实现类中 " }! G% o& G$ z4 |( d7 n4 k; l6 b
class ATL_NO_VTABLE TLoginServerImpl: 。。。
) j. @$ @0 {7 E+ z: _( S& b. G{
* y; d" [$ S5 L// ILoginServer
' {6 u2 Z# v: i P) x3 `+ M1 |protected:
- d0 C) o1 G6 Y0 z STDMETHOD(Login()); 0 B" D+ _ N' w0 T5 a( g
STDMETHOD(Logout()); , _/ \& z8 f2 `2 t8 {0 ?$ L6 [
}; 8 H. W( K8 u: {- @$ Q& P
然后简单实现之
6 y- n/ l. p; x7 I* l# e__fastcall TLoginServer::TLoginServer(TComponent* Owner) ( e8 X7 ^# Y" Y1 r( _& }
: TCRemoteDataModule(Owner), FLogin(false)
) s# F0 q% W" \) }9 D{ 6 L- l0 v3 j! \
}
) F7 k! c, \5 ^$ M//---------------------------------------------------------------------------
, Q/ M4 M8 U. ^8 ovoid __fastcall TLoginServer::SetLogin(bool value) ( F; x. a' d- |; ^1 i
{
# I3 @- }! H) S8 |! Q if(FLogin != value) 6 Q0 T2 U+ {( [. }! `
{
! y' O) \2 a ~: x FLogin = value; + F( G/ j. G) X7 J# a* l
dspAnimals->Exported = FLogin;
# z C; d5 h, B6 a! W } 7 v1 [( C. m2 b) [$ o: |( `2 }
} ' P* L/ U' U3 j F% Q5 |& o" v" e, S
//---------------------------------------------------------------------------
( Y$ w f* s9 M) x0 L2 gSTDMETHODIMP TLoginServerImpl: ogin() 3 [9 h, D5 }) ~- W
{
1 R# h5 `/ |1 p( O' u m_DataModule->Login = true; & X( v- J* v' k& F
return S_OK; 6 r/ H' |3 G. B/ L+ j, |. B1 |/ l
} ! L: d4 s- `- @% E& T' d8 T- |
//---------------------------------------------------------------------------
$ Q; d9 F9 ?# hSTDMETHODIMP TLoginServerImpl: ogout()
1 Y7 n3 l3 x7 {) I" q4 X: m{
( Z& [( n& N9 P& S. k m_DataModule->Login = false;
; c2 w% B! d# D: b6 I+ f3 U return S_FALSE; ! R j( `: T; u+ [( {3 S
} 6 W/ o; q, b' ^( v
//--------------------------------------------------------------------------- 4 R' `8 y3 t, Q* F, m) I
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) / [. E- w; \0 M& m
{
$ v2 }; G. \4 {7 Z; L2 ^ return FLogin;
' ?# K4 n6 `( E( k}
9 v/ s+ r2 j& C2 v4 T0 jeasy 这样除非你的客户端成功调用Login方法,否则他是无法使用 * g: u; U3 H" W8 N# E/ F! X! Q8 b
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
+ X' g- t' ?4 f( W
: J6 c" `1 k. ~$ ?比如客户端可以用如下代码 % n) Z3 E( _+ _4 x; R2 |) f
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); ; w/ d* F; ]: p& h8 K2 u
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; 1 j% \- M" `! w! i/ a8 e, i
dispLoginServer.Login(); ( @& k ^9 X V: \2 H7 d
DM->cdsAnimals->Open();
$ A; R4 J8 p& e. |; }7 q' A实现注册,并且打开相应数据集…… % ]4 R% @$ O2 I7 w! \7 |1 S( h
# m1 @( B( F$ M j- j
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
+ ^; B9 M/ I% E v4 l; e! z5 V) \7 J绝大多数程序应该足以…… |