|
7 X& ?! J, p( I$ \* ^: g
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
8 T* q8 h, ~1 |0 J) y) f4 m' h/ \- C在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 ; U8 s% S9 o! O3 |8 Y6 H% E2 P& ~9 I
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
6 k$ @+ s- J, n5 L. K% M现就我以前对MIDAS编程的理解,做一个简要的介绍, 7 u7 K9 F# l3 y
以后有时间精力再详细举例说明
% P# z; \* M. M+ ?* |5 Y
7 s" @/ V! ~# H) Y+ Wwin2k中dcom的安全级别有7种,其中 # B) }( H- v* B6 N, ?
最低的“无”安全检查暂且不论 3 s5 x. m! P3 K( X, B/ l) p( u# l
默认的“身份验证”服务方式几句话也说不清,
+ h& [% A. S3 n8 p$ ?$ H. [先放到一边,看看剩下的5种 ) E' Y, W4 ~3 _ T
& q" a. J! e! S, W6 f1.连接,仅对初始连接进行安全检查 3 C8 b8 p s+ Y$ ~4 |
9 | k( g, p2 y' h. E 因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
& b) v1 u/ H0 z; {( e& O5 }8 q 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
/ M- l" g0 c: k! l 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 0 F9 e* S9 Q1 o1 h
interface ILoginServer: IAppServer
( u( h1 m' e( c+ ~{ 2 w* o7 b1 n, {8 x
[
D; H; I" R" L- v3 S id(0x00000001)
# F: W Z! R2 ~ ] - X( u( Q# b! y) |: K" O
HRESULT _stdcall Login( void ); ' X8 w1 J1 z4 q; @! h0 d+ {- I
[
; |# Y% V) S; K7 ]3 q id(0x00000002) ) J+ T$ e( X, m3 h( T2 H
]
0 V( e" X" U9 a* M0 n/ v HRESULT _stdcall Logout( void ); 1 n/ r! p3 P: W" N2 M
}; 7 S% r! I5 a3 B/ V
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider
" S) p- |2 U& c( B 的Exported属性关掉即可让客户端无法看到服务器端的provider
5 x; f* ]3 O( C: _* J2 n: z 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
0 P4 {- S/ w% ~% @. _. a6 f6 Z 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 ( w; J: W" x- \
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
1 {" l- e1 _, t2 \/ ~+ j 都是通过相应的ProviderName来指定的,如 9 z1 p4 o: n: R- a+ X* H! E
virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in ! w: {' W2 n' C! _6 m
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
$ }; M9 f6 T6 j( et & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; ; R8 U0 }- x" t; N5 ~
取得数据方法的第一个参数就是你需要操作的数据集名称…… ) g% }/ R' K* _0 M$ I5 w
如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, ) _8 }( c7 a8 D5 u
则客户端看不到任何Provider,也无法通过其ProviderName取得数据
* ~1 D' |- x c# J9 c6 m$ f& Z6 H 例如我在服务器端的RDM类中加入 + w* q+ j/ E4 S+ E
class TLoginServer : public TCRemoteDataModule
- a# R# |. U7 [2 ~4 {# W4 q+ B0 c{ 4 i8 v) w3 p7 v/ }; t
...
! a4 Z& F' ^+ M7 w8 n( m0 @private: // User declarations 3 w9 M$ {" W4 t& J7 f/ `7 C
bool FLogin;
, { ~4 _! i( M# Q# d# B void __fastcall SetLogin(bool value); 2 U- {% |4 p4 T3 O3 b0 c- W) @# s
bool __fastcall IsLogin(OleVariant &OwnerData);
' k) u, k: C7 `- ^/ a...
o& o& d3 D" z- e1 X__published:
' [* R& t3 @ q" w __property bool Login = { read = FLogin, write = SetLogin };
/ m: D4 j" D6 ^9 Z}; ' b+ k& Y/ P$ Y
在RDM实现类中 2 m2 V3 C B. \7 S5 q
class ATL_NO_VTABLE TLoginServerImpl: 。。。
' S# p; t, |3 L5 R; ?0 H# j{
8 N+ G8 i" _0 m/ c, a9 g// ILoginServer
; U; C: Q9 F" F0 S! dprotected: 5 K4 c, V1 N+ l! @- K; }6 A
STDMETHOD(Login());
4 w- e- Q% ^ |% W p STDMETHOD(Logout()); 5 ^# `* R# Q& C5 j. N" h6 G/ Q
};
( b% V$ j! }% o7 B/ V7 W! x然后简单实现之
# I* e" k8 s3 B# L* f& k" m__fastcall TLoginServer::TLoginServer(TComponent* Owner)
9 j* g8 Q+ M1 C9 l1 M : TCRemoteDataModule(Owner), FLogin(false) : _" {" G: J" S9 ?
{
+ ]. N# s! r: q1 b$ i) {3 m}
: C) R2 q" D: @4 k$ C! B! C2 S3 e//---------------------------------------------------------------------------
8 Z8 v* ~5 Y2 _% x( K- O; `void __fastcall TLoginServer::SetLogin(bool value) 9 N, w) Y5 I3 r
{ - h5 }% S) R7 V! a- c
if(FLogin != value) 3 _" {- f: E1 c5 r% O& t+ K
{
: @3 e& I* h9 M& ~ FLogin = value; / k, I1 _# A$ e2 X6 C8 @% `
dspAnimals->Exported = FLogin; 2 @' G/ w6 X- j' j0 q/ o
}
/ x& t/ \$ H; M$ M7 c8 D}
! Q8 m! e+ N' g5 s% q//---------------------------------------------------------------------------
0 S2 z1 r- ] q# M/ I' _STDMETHODIMP TLoginServerImpl: ogin()
4 P" ^/ d6 p" L/ L{ ( V5 J! G* C( E
m_DataModule->Login = true; 8 M# z( ]& Q7 C+ K9 G: A6 i
return S_OK;
3 m9 U1 K6 H$ j7 ?& X! R} " M/ H4 P0 w: l+ I: F$ C
//---------------------------------------------------------------------------
$ i% ?, N$ z+ L" h- x" oSTDMETHODIMP TLoginServerImpl: ogout()
. Z X `( d$ [{
9 a& J, B1 u5 ^/ M m_DataModule->Login = false;
. F. s, Y3 p% V return S_FALSE;
( h, D7 `3 |0 L0 |* Y5 [} . q8 L/ X; _0 v5 y3 U8 v
//---------------------------------------------------------------------------
! b4 V: C. L" k# ]2 J( ^bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData)
( v- ~- ^ M5 v{ # e5 k% I! Q& J, A: V8 L7 E# W6 ~
return FLogin;
% k# Q W( O% w9 m& L- p0 ]} ! H1 Z0 w9 _2 m- y; |2 m+ O
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用 ( R. Z. u' [' P: f) b# w
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
o+ K8 y6 Q6 m5 W2 g5 | $ L% a* P: q5 q. Z# ?. {# O
比如客户端可以用如下代码
# T% G N$ x3 c3 [ IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer);
( B8 u0 Y' A2 q' v3 ^' h! j. _% K% X ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; 7 h2 D1 t0 [* U7 K
dispLoginServer.Login(); 0 ?. U# y( S3 h+ Z0 I& [
DM->cdsAnimals->Open(); . i5 i& f% a. t3 f: Y
实现注册,并且打开相应数据集…… $ J1 [/ G: ]- ?
8 a5 q- @. F6 J! [因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
( z: }4 @' l5 q7 F% ~. [绝大多数程序应该足以…… |