|
) k1 `; C+ n0 _: m# A
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 - [7 D: u% X' k/ N9 v; ?
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
. L! A1 s- S8 s2 d安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
) s) e" A8 k. B现就我以前对MIDAS编程的理解,做一个简要的介绍, " I' E* @$ v, y6 E$ L# Y5 ^
以后有时间精力再详细举例说明 1 k6 U8 s: O% I$ g
6 U: K. F$ W# G8 D
win2k中dcom的安全级别有7种,其中
& p2 H7 _; Z/ P# ^% m Y2 l: Y8 c最低的“无”安全检查暂且不论
9 D$ }2 H# D) ~5 t( j默认的“身份验证”服务方式几句话也说不清,
8 _" S( ~! E% k先放到一边,看看剩下的5种 ) {+ q: C, S E5 b5 C+ u9 p
3 A& @, Q3 A5 [+ A6 ?5 P
1.连接,仅对初始连接进行安全检查 7 q& j. `( O5 ^4 I& x5 X
& j6 a$ `$ t. o* X" f0 p4 ~" U
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
4 e& O# \3 S) A0 D 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
* s( v. Y! e% ~! b! A 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 6 u; P0 ]2 M9 _( I9 s7 A o3 Q
interface ILoginServer: IAppServer - ^; N3 b* ?) ~! e8 w5 y' l
{
4 d8 Q* {/ o1 E7 D" w8 c4 C4 V [
3 J2 C! o! c: c; g; r: U, }3 G$ g id(0x00000001) & s8 q# `2 E* {5 P5 G: R3 n
]
$ G! G- H4 ~5 g# r3 B3 a/ w HRESULT _stdcall Login( void ); ( V, K( s- c4 [2 {
[ 2 y2 h- P8 t( r. ]: _8 x9 M
id(0x00000002) E2 S1 j9 D! b$ N; N, \7 E
]
( v/ b2 o! u) p# Z# y9 \ HRESULT _stdcall Logout( void );
& Z- L+ ?2 \ \6 n2 \0 W}; & W& }; V) [- L$ u
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider : u, G9 @* O3 G$ f
的Exported属性关掉即可让客户端无法看到服务器端的provider " E" r* c" ]! w' f( r) ?
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问 ! O# c! G2 V k$ L) n
所有的Provider,如IAppServer::AS_GetProviderNames可以取得
. [( c4 J( {2 { e) y' {1 N1 C" y 服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
4 U; V) V [+ W 都是通过相应的ProviderName来指定的,如
2 h9 P+ R: r+ ]5 z0 g; A5 \5 bvirtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in
7 g) i1 D3 t" c0 Lt Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
s1 H' z, m2 }8 n* b. s& G! Ft & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; 9 `7 i4 \ }0 E( j
取得数据方法的第一个参数就是你需要操作的数据集名称…… 5 m0 A' Y: q* Y0 W2 Q- B! {
如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
6 d3 F! h+ |7 w 则客户端看不到任何Provider,也无法通过其ProviderName取得数据
$ Y; n" E$ L1 P- l% c 例如我在服务器端的RDM类中加入
% I/ s1 b* j- p5 p' K6 Gclass TLoginServer : public TCRemoteDataModule
[0 }/ x. b2 p* }{
5 ?# o; E3 l8 B% P* o... ! B6 b8 {+ ]6 Y( \7 c, |# {
private: // User declarations
& l; A# n4 ~6 c4 r bool FLogin;
# m4 ` a. b7 t2 i- t void __fastcall SetLogin(bool value);
: w7 X1 R4 A( O) A& \ bool __fastcall IsLogin(OleVariant &OwnerData);
0 I; b% W: D4 z; x7 s... ; T. S3 ~, ~, O8 g9 i/ b
__published:
; K, Z6 X/ r# e; G9 i8 P5 ^" S __property bool Login = { read = FLogin, write = SetLogin };
9 S) g4 N* y0 g3 r0 |+ B8 ?2 v/ f2 g. n; f};
- W; {( W* I5 h8 o" B在RDM实现类中
8 g; r& U# j# ~, p/ g3 o5 Mclass ATL_NO_VTABLE TLoginServerImpl: 。。。
0 w$ @' i% ` w% w{
; P) f" ]5 J/ [// ILoginServer 1 i/ I8 P2 C8 @
protected:
/ i: O/ |4 x+ @* G% b+ O STDMETHOD(Login());
, Q6 J4 {! n% A W) D" i2 l0 Z STDMETHOD(Logout()); ( w4 Z8 c" Y$ w7 Y# Q* \, v3 E
}; 0 O7 {9 ]& k6 S* X
然后简单实现之 ' i* |8 A9 w9 `- Z
__fastcall TLoginServer::TLoginServer(TComponent* Owner)
: X) i( l7 F% E0 F" u% G7 U8 @ : TCRemoteDataModule(Owner), FLogin(false) ( L" h4 s- M* _2 c
{
- a4 b8 \5 ~. n}
5 F2 |) e3 `6 Q//--------------------------------------------------------------------------- , Q# Z9 T+ `# [( K
void __fastcall TLoginServer::SetLogin(bool value)
?) d; I4 _ x1 i$ V% O{ ' r- W3 a* M6 b1 s' k9 Q c
if(FLogin != value) / K9 ?" Z" M; ]* p" C
{ 7 ]3 c5 Y; P1 f) B
FLogin = value;
4 s5 c; L$ C M x C7 b dspAnimals->Exported = FLogin; + ?' l- o# c. L% d: i+ Q" {
} - S! H6 `; p* e( y
}
3 _0 z! |6 P- q+ t3 F9 z//--------------------------------------------------------------------------- ! n, |" F2 O4 P3 E5 c9 I3 S# w1 H3 g
STDMETHODIMP TLoginServerImpl: ogin()
1 g- e! j, X u4 u$ H# v{ ) t& M& i3 v: |
m_DataModule->Login = true;
" c9 o& o1 |) L8 P return S_OK;
7 i$ q/ T; N5 p3 l} 2 Z4 K/ O: w, |! U& J1 A1 V/ c! f
//--------------------------------------------------------------------------- + `% N( k7 b$ m
STDMETHODIMP TLoginServerImpl: ogout() $ |* w' R' P: x# t! C* b
{
' t+ ? w& Z, z; z m_DataModule->Login = false; * C+ p, B5 M6 E; r
return S_FALSE;
. g* [6 I0 w. x}
- x9 E; I1 y1 o: P/ c- n1 g# g//---------------------------------------------------------------------------
2 P" G) j: K" L. y d$ b: T" ~bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) / E& x5 W9 @2 h3 O# }
{ : w4 @; C4 o: S$ d1 {
return FLogin; . A* c) R( L5 J$ h3 Z0 _" z
} ) m) y5 A2 P0 l* B3 Q
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
' j( t) K" m' t/ AdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同 # `5 V( j' t. r7 R+ T
# V- Z% u: L+ V1 e/ B比如客户端可以用如下代码
* |! I9 t8 M' _) M" N7 v* {8 m IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); " T8 i% l9 ?$ i* f& G0 i
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; , s: ~9 K# G: `( p( e3 X4 F+ ]+ X* i4 Y
dispLoginServer.Login(); 8 J/ M" [- V, K4 \
DM->cdsAnimals->Open(); ) d- P9 b }9 s
实现注册,并且打开相应数据集…… 8 \ W" B- m' C) @5 g! l2 H
5 a2 ?5 L8 w- X3 ?0 v8 p4 W
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 & s& t; I& i: `1 ]: _; C3 w5 r* L
绝大多数程序应该足以…… |