|
0 s0 t8 x8 d$ g( p6 j: C% w
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 + p o- |, @6 {9 _/ U9 B* G
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 + x. O2 b2 ?; Z! O* ~
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
, G/ ?/ d; q* M现就我以前对MIDAS编程的理解,做一个简要的介绍,
5 s, I$ D- p; o$ s& S/ G. x( g以后有时间精力再详细举例说明 4 Q% E, F( K( c8 Z% A( [1 Z+ @9 @6 f
2 {- w# J' o( t& \ O# `
win2k中dcom的安全级别有7种,其中
* |" z4 z) F( T5 S# i& {. q最低的“无”安全检查暂且不论 : O* t+ J& B( I5 J( W% I) m
默认的“身份验证”服务方式几句话也说不清,
0 f2 T3 b2 @0 D7 k3 s0 v# d先放到一边,看看剩下的5种 6 ?3 d2 i: N/ G! A- P
+ s" r I9 k; @+ V7 n6 X1.连接,仅对初始连接进行安全检查
5 R6 Z2 e) P7 E+ C 4 j3 ~8 i( b! I- H6 b
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
" O, N5 o8 u* G9 o) Z! d) \ 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
( D- B" g; V$ ` 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 6 T% q- x( G' x. V
interface ILoginServer: IAppServer
# ~: q) F6 j1 E; m( y4 v{
* |; t8 y8 d. m' ?' O E [ ) g# F& {% H! g% {
id(0x00000001) 9 r0 N- ]4 K P0 W, i% @, |
]
6 j& m- \. h- s5 U6 t) S HRESULT _stdcall Login( void );
: i7 {( }) Q# e) U% k, P- ^$ m- o [
+ k* |* J$ z0 L2 }3 n! A id(0x00000002)
, }' C" v. {' n ]
$ ~+ I; K+ s( z7 B \9 l: L HRESULT _stdcall Logout( void );
8 J5 K+ u0 C5 {# H' n) z8 d4 W* Y}; 5 C+ T( o9 F/ V
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider # `& w7 U+ W0 V- f; h$ t' P
的Exported属性关掉即可让客户端无法看到服务器端的provider & { `2 z, U. L
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
+ [+ Z: o0 H7 U$ {' ? 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 / \9 ~. R8 L7 }$ e3 }8 j+ @
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改 0 J5 H: T- p7 t: r2 X2 o: K& Q. l3 Q
都是通过相应的ProviderName来指定的,如
$ g. K* l' O3 t3 f+ n; w3 h& @virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in 8 m- G1 Y9 P' R v' }6 n( ?
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian 5 a) m6 e0 Y- u$ A% g- @- E
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ;
& ` r; G4 z3 b0 l: ?: h7 y 取得数据方法的第一个参数就是你需要操作的数据集名称……
4 S$ T! _/ `' a. ^7 z 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
# w8 k* W) J" P, A4 t7 H2 w; P* t 则客户端看不到任何Provider,也无法通过其ProviderName取得数据 ! i2 _* T% A. e( ]2 y; Q
例如我在服务器端的RDM类中加入
$ h z/ g0 x8 W& G0 ~class TLoginServer : public TCRemoteDataModule 3 }* P' m( K$ p1 p. H
{
' v9 k2 g; i) V' a2 E...
l. Q; x0 P* D" H+ m2 ^9 t! b& tprivate: // User declarations
4 z/ y# h, \1 o& O! p4 u bool FLogin;
' U( A5 I4 Y% M/ _2 g( v void __fastcall SetLogin(bool value);
% x% o: a! } U3 L# p8 v bool __fastcall IsLogin(OleVariant &OwnerData); 1 Q t- |6 W: c8 }9 X
... ' B" e+ ^5 d2 k8 e; R6 ~* b
__published: * ]- R( Z; b7 O$ i+ X7 O
__property bool Login = { read = FLogin, write = SetLogin };
1 I3 T9 p0 A. @0 P};
; L" k0 O6 h- Q( r8 S在RDM实现类中 9 F2 h: Y6 \* f7 k9 [( @# {
class ATL_NO_VTABLE TLoginServerImpl: 。。。 * V) Q# p5 n9 u+ @ ]/ n/ M+ g
{ 0 C6 j- L+ k! u' D I
// ILoginServer
c( z5 D) \) |$ e1 tprotected: 5 A5 g9 S' O- S# v* n" x9 m
STDMETHOD(Login()); ' h. `# b: f8 l; a+ k) C$ ?5 T& `2 V
STDMETHOD(Logout()); % l/ I. U' M$ p O$ y c" }
}; ' g' L5 j3 u4 a: [( s3 E a! Q
然后简单实现之
* r4 U& I2 u. I$ r( O__fastcall TLoginServer::TLoginServer(TComponent* Owner)
# @- h% d2 X: v4 h. v9 ^1 R : TCRemoteDataModule(Owner), FLogin(false) 2 u- Z0 E1 }" ?
{
- i0 }/ s, J" a1 S0 N8 K} . z) b+ }) c3 n- X! n% q+ I' q
//--------------------------------------------------------------------------- : _0 e7 j3 b! g2 u, }7 H# k
void __fastcall TLoginServer::SetLogin(bool value) 9 \- n T: k. `
{
1 N3 l p! d$ M8 L1 v if(FLogin != value) + y$ V7 @3 o: n' V2 h
{
- z1 p, r9 n/ C% ]: Z6 i6 ?+ V FLogin = value; ( S; r7 N' n; Q2 t0 s, t. B5 K4 \
dspAnimals->Exported = FLogin;
+ m4 N. X; { E( X3 ]5 P }
4 j+ N8 W5 H) L* H/ q}
: V. q, Y+ r7 K; A5 L6 |# C//---------------------------------------------------------------------------
# B1 U" T1 d' oSTDMETHODIMP TLoginServerImpl: ogin() : }: x4 b' w8 h1 S/ w
{
) |1 R$ n- r3 D6 V7 p m_DataModule->Login = true;
+ T7 w t# q8 t. h0 d7 \& O return S_OK;
- J5 z. u: b: `5 W/ _, Z- x) b} / c+ h/ v$ B$ @4 \5 x8 d2 H$ L {4 b
//--------------------------------------------------------------------------- o, n9 n0 `( t' Z0 c: \" t
STDMETHODIMP TLoginServerImpl: ogout()
# Q" l7 ]% c/ X{ 5 P4 Q0 K F8 B' Z) Z8 ]7 C
m_DataModule->Login = false; + y& G0 Y" A7 T; _
return S_FALSE;
I# M+ g+ O% L- x} + z! c! A* O; ~! O2 l- o
//---------------------------------------------------------------------------
) h# L" L% G# H9 r& U0 pbool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData)
4 L! |5 _: P) v c, k{
; {5 y" N6 i- l) E return FLogin; * R% c: k2 K9 `, e
}
H* J* G3 { }! k; x. Neasy 这样除非你的客户端成功调用Login方法,否则他是无法使用
4 G) z3 E9 g% p, R% R' g9 zdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
& T4 w2 `2 z8 V; l
/ N# d" i* Y' \比如客户端可以用如下代码 & a3 @/ P6 j" K8 d- F1 c0 Z
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); ; a8 O) ^! ]* } @6 J
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; , E) W# K( v5 U) |1 m
dispLoginServer.Login();
0 j+ B' \( E& g+ x. m& j DM->cdsAnimals->Open();
+ W& G) m& Z# A实现注册,并且打开相应数据集…… 8 _" W5 ?! e- U* D8 y) W& y
1 h/ a% l: _! i- u4 K" i- _
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 % ~, Y# W% q* ?& S- c: L
绝大多数程序应该足以…… |