|
! U) @; z* O% B+ B( g
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
4 {0 g! {; f, D u在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 " l+ Q# q( `/ s; p& e
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
3 k" |# w: |6 R h, q现就我以前对MIDAS编程的理解,做一个简要的介绍,
9 J7 \$ G+ [ ?以后有时间精力再详细举例说明 2 b* N- x- u) c' ? g& J/ c- p
4 D# K6 Z! w2 F% mwin2k中dcom的安全级别有7种,其中 " M! u$ p7 K5 Y% p: W
最低的“无”安全检查暂且不论 $ E. G; J3 S- X( ^
默认的“身份验证”服务方式几句话也说不清,
1 @! H+ _- f' _* P7 Y- }' [; F先放到一边,看看剩下的5种
P6 X* H9 c8 E' ~* @# n! }+ S' {- L
7 r' O* D7 p D! x, F4 Y& b5 W1.连接,仅对初始连接进行安全检查
* _- s7 c% r/ X; E & |# w; y7 e; w& g( g
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule 7 ~5 e& W- Q* U }% o
实例检测一次即可,实现方法很多,这里简要介绍一种最简单的 6 w7 U; \8 }1 o7 G9 d
可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 $ P' S/ ]" [+ q) J
interface ILoginServer: IAppServer
2 v" y D- n2 ?8 Q{ + Q! o* D1 z" Z# X% f8 O, `% m
[
: W$ n, \3 m! l$ g- g e id(0x00000001) 9 k4 s' P! e6 f+ V$ e
]
" m. K4 O' y" Z: g4 W* a HRESULT _stdcall Login( void ); ) l& d. Z3 c D" O9 ~1 ]* W, A
[ 5 j+ y) [ \0 f, o9 c$ W
id(0x00000002) % e. H1 ~) f3 `6 r/ C* j1 E, m
] 3 { x% V1 r/ k6 J' g
HRESULT _stdcall Logout( void );
- ~8 n1 Z0 O5 t! c0 O+ r2 U}; $ v) o, P% U2 c5 c; K
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider
J3 a, w8 G1 A5 T$ X6 I 的Exported属性关掉即可让客户端无法看到服务器端的provider ' i) ^( X- L3 X. K% B! ^
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
4 x* g3 p' L8 s7 }% K 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 3 }% j) p: C) }3 u) Z3 s
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
, n) M/ U7 v3 } 都是通过相应的ProviderName来指定的,如 9 z8 }% w5 ], W! a! o
virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in ! U! Q# y. a1 _6 }; `( i
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian # o+ q& y. m8 A( y* v
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; - g2 M/ m7 U& i. I
取得数据方法的第一个参数就是你需要操作的数据集名称…… 4 ]! n s+ s- s# K) ?5 `
如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, ) a& e/ j# B0 w3 c# T( ?) r
则客户端看不到任何Provider,也无法通过其ProviderName取得数据 : ^/ y! _% k0 C/ {0 B
例如我在服务器端的RDM类中加入
) K% P' @' ]7 Z1 j4 u/ H- G% x; m7 bclass TLoginServer : public TCRemoteDataModule
: Q7 W1 }; R# K) m0 W' ~{ 3 [/ Q- h" i% R
... 4 G r4 j. T+ m! e) _& _+ Q0 n
private: // User declarations
, J* X3 n4 p0 q; a bool FLogin;
; z- f. y- V6 U void __fastcall SetLogin(bool value);
5 ~: @! G J$ e. { bool __fastcall IsLogin(OleVariant &OwnerData); $ E4 b" x% Q2 T, d; v C6 T
... - D2 ^; h+ F' H; N5 z' x6 @* C
__published: % m2 t, F1 h, ~: E
__property bool Login = { read = FLogin, write = SetLogin }; . S- g1 C9 }4 H! B# o5 t7 v
}; # B3 A( `( u& _' q4 E* K# I9 n
在RDM实现类中
3 ~' K9 |4 q& i% D$ g H3 v5 p3 } |class ATL_NO_VTABLE TLoginServerImpl: 。。。 3 ~: p# \7 d$ J9 K. Y
{
6 C9 v: p4 q, G8 G$ D( C& w* q! [8 S// ILoginServer
0 G) x5 F9 H4 w4 pprotected:
! B4 h7 k7 J: [/ j. ]3 v7 p STDMETHOD(Login()); 6 k# m' W( F. `
STDMETHOD(Logout()); $ x, `/ F: N9 n4 }- ?
};
% f+ A a+ z3 V" J2 e然后简单实现之
& e6 u8 H* C. u6 Y! v- X__fastcall TLoginServer::TLoginServer(TComponent* Owner) # p1 d7 O+ s: M4 p. ~2 M: Z
: TCRemoteDataModule(Owner), FLogin(false)
* t$ m; p3 R; t6 s; g1 r{ # d# @/ r* b4 e; a+ ], x
} . E% l" V: x, }; F
//--------------------------------------------------------------------------- , m) p) j( v7 W7 j. o+ s' B
void __fastcall TLoginServer::SetLogin(bool value)
d) j- e/ M% D9 w{ * U# E9 b- @. o2 q
if(FLogin != value) , P! v" t S3 ~! A8 D& H
{
+ j9 m6 c0 p8 w3 M FLogin = value;
* m3 g2 V/ }( V1 Q8 M8 o dspAnimals->Exported = FLogin; ; L! m0 d$ {& `2 Q# S0 S' g \
}
0 j3 i, M" O2 G; U/ e( a& @6 r3 N}
6 Q1 T* X4 X6 t3 D7 C# Z& s6 e//--------------------------------------------------------------------------- : k" h t: V: v* g1 b; j
STDMETHODIMP TLoginServerImpl: ogin()
; N! A i/ }/ g8 I{ : h+ R6 D% K) @3 `8 o
m_DataModule->Login = true;
4 R+ D! S0 m$ v' V! c, `) Z0 |4 p& { return S_OK;
# s# l+ a0 k+ j) v; M" o} - g2 ^: y5 c4 c7 G/ |
//--------------------------------------------------------------------------- T7 [8 |) B9 y. }
STDMETHODIMP TLoginServerImpl: ogout() ' a' T1 M) h, j
{ % w1 ]: N: Y3 x0 E1 R H
m_DataModule->Login = false; % q" I; K/ ~: A; G) d5 `; [
return S_FALSE;
7 E5 N! U" ?& c9 ?0 y/ G4 U} * ~& `( X t9 v3 g* B+ {9 r
//--------------------------------------------------------------------------- & P. b4 ~$ }( c: n" }# L7 O9 G
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) . o$ @7 d4 l! v4 u8 u
{
( x4 r+ N# _2 N$ j e return FLogin; % \3 N; y+ i+ E
} q9 Q# ~( b4 w' _- m
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
. P r) w! V6 G3 K7 E; T* f* P8 b/ HdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
" l: |! ~3 t% Z: ] Q! @) `
2 Z8 |7 C( f" V1 P" |4 A+ ~. O% t比如客户端可以用如下代码
( b0 H$ m1 }1 I0 Q Q0 F IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); 6 m0 o/ ~5 P8 E4 S
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; 6 `5 Q* [% F' J+ O6 h' z/ l
dispLoginServer.Login();
/ g, _- N1 N5 N8 N DM->cdsAnimals->Open(); # B4 l+ Q# V8 i) K! O5 L1 O' Q
实现注册,并且打开相应数据集……
- U3 R3 B) R, q: p! x 8 `" c2 ?6 X5 I; {8 L/ Z( D" K2 d
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 5 m, ~/ X) b( a" m7 C
绝大多数程序应该足以…… |