|
* _3 Z( q. s, M" U9 _
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 ! d3 P# {; }3 ^) ]2 r# H) J
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 " q7 n) I/ K" Y7 m
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案 w) v# d6 X5 V6 l: k* L
现就我以前对MIDAS编程的理解,做一个简要的介绍,
% z! R4 ^+ k2 [; c7 k6 w; Z. l2 T2 H以后有时间精力再详细举例说明 0 a2 A: @3 P# Z5 _5 N) V6 i
# S1 C" g! J: rwin2k中dcom的安全级别有7种,其中
' |1 Z) n$ T7 L最低的“无”安全检查暂且不论
2 g) R. \+ l }: T' U" }6 F% b/ ~7 i默认的“身份验证”服务方式几句话也说不清,
# [/ z6 `3 P" v先放到一边,看看剩下的5种 " U+ }6 d2 ^5 E+ N1 d$ V
4 B7 R! u4 H- ^* C2 h
1.连接,仅对初始连接进行安全检查 ; U. U- H H8 o6 N7 ]( i& H9 H
0 a7 a+ k, Z d, g
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
" L. G5 u, j: S5 G& P 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的 " w% D5 | I" ?
可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 $ ]3 l! m" m7 O- K9 ^
interface ILoginServer: IAppServer
& d7 i6 L8 Y9 S; o4 c{ " O; E7 b1 h% L6 S) \
[
4 H+ a! T: i2 ]' b id(0x00000001)
4 \) I6 R4 g P; e3 o: G0 L$ a ] , Z& }/ m3 B3 Y6 n: M1 z' L
HRESULT _stdcall Login( void );
4 ]% r! m% E1 n! g [
* o8 v2 C9 ^& \2 {6 `- x4 p# ]6 f id(0x00000002)
; S! l f: R! z/ T. b i2 H ]
- q, y+ q; m$ ~, S: ?& K+ U0 v3 i, m HRESULT _stdcall Logout( void ); ; }9 _# S: m; \( Y& K
};
$ `$ x6 ]+ d+ H$ w 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider / A- \( {! M1 |( r; C1 F
的Exported属性关掉即可让客户端无法看到服务器端的provider
$ }0 f( @, Y( F% k4 E 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问 " d' R# ~ e0 ?$ o2 c
所有的Provider,如IAppServer::AS_GetProviderNames可以取得
6 z% I. N$ ^1 m) ?9 [; j$ K 服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
) c" x L# ^1 x$ r6 ]" i5 l) t$ [$ ? 都是通过相应的ProviderName来指定的,如 ) M5 I' o# E3 u1 i9 u
virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in
; R4 C g5 K! v9 N$ `/ lt Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
) s- T4 t8 ?% N2 vt & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; ! R1 y1 Y8 E* G+ v5 Z- ]5 i
取得数据方法的第一个参数就是你需要操作的数据集名称……
9 y7 l N; l7 l& s( ? 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
( ^. Y$ }; Y3 y2 a1 P1 s 则客户端看不到任何Provider,也无法通过其ProviderName取得数据
' G: i6 ?0 r ^5 e 例如我在服务器端的RDM类中加入
; T; ]: y$ _/ C& k+ [class TLoginServer : public TCRemoteDataModule ' k5 X+ K5 k' J$ v
{
9 @9 ^8 K. W: p X... 5 t+ O7 [7 q" C3 @2 f3 _4 R
private: // User declarations
9 A! x4 D2 m5 U: `/ z5 j5 V bool FLogin; $ p1 ?2 B" C B
void __fastcall SetLogin(bool value);
6 V8 O7 m: `6 P' E8 J$ X! p9 K bool __fastcall IsLogin(OleVariant &OwnerData); ) \5 F9 C+ ]! ~7 M3 k) | n
... : K& b( L/ j3 W7 D4 |
__published:
* A+ l0 Z! q7 x1 X! Z! V1 M __property bool Login = { read = FLogin, write = SetLogin };
2 P( Q8 f# x& b2 r! r}; 4 O3 K2 l4 G4 p3 c
在RDM实现类中 / R# ?! v% R) ? U+ k6 v C! g
class ATL_NO_VTABLE TLoginServerImpl: 。。。 3 n, q% h) t# e! [
{
6 h3 {1 G0 o. {6 P// ILoginServer
/ r& x% v: Z5 m( ]5 o( @9 q; v+ L8 Mprotected: - s/ X; i2 F0 f9 g% S
STDMETHOD(Login()); ! E8 G' O4 d# r( a" x! R9 q
STDMETHOD(Logout()); 4 a3 U1 n x! o1 t+ a$ q- d, w9 `
};
0 t- Y& i a% v: Y然后简单实现之 5 k. C, @; w$ T9 i
__fastcall TLoginServer::TLoginServer(TComponent* Owner) + N# \, u0 m; F" J+ @
: TCRemoteDataModule(Owner), FLogin(false) ! `- N1 s. v7 }0 Z3 {5 v/ A
{
/ t8 n0 m+ E2 }8 N} 8 b4 T; o; H& N$ Q- }0 {" W+ u
//--------------------------------------------------------------------------- D& c. F- x. p ^
void __fastcall TLoginServer::SetLogin(bool value)
8 u: h* o% E, P1 i{ 1 ?2 e* N" v2 I1 n/ ?( ]4 g" {! C
if(FLogin != value)
0 D j# f3 T' @5 \4 Y1 N; L. V { ' S% k; i3 v) `5 A
FLogin = value; ! ^) ^0 @, r+ |# ?! x* C4 q
dspAnimals->Exported = FLogin;
4 @9 k/ M) Z7 u& b } * j4 o V3 X0 E& v0 U
}
- d% V; ?9 s5 J3 o//--------------------------------------------------------------------------- # ^# ?# h/ |! Y, O
STDMETHODIMP TLoginServerImpl: ogin()
, u( | l* B3 n{ * w0 w7 T8 O i( t P
m_DataModule->Login = true; # T: o8 s8 Y9 u5 L( q% H. ]
return S_OK; 2 J* q- u! O0 C5 F" y; p$ M8 ^
}
1 p0 q5 X& o7 z6 D; t//--------------------------------------------------------------------------- + R6 L7 R6 n& A/ y1 Q. G
STDMETHODIMP TLoginServerImpl: ogout() ; s" T/ |2 |" L. h/ k
{ ) y; b. w: L: R
m_DataModule->Login = false;
! X4 E& u0 h$ m* p, L) U return S_FALSE;
. s( e8 U; ^% P7 I+ F9 D}
$ z& f3 p% y& v8 d- E//---------------------------------------------------------------------------
( u- G- a! e* y1 V8 t# Xbool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData)
6 x# {: R1 r3 o! C{ , r3 ^6 p* `* s5 l5 t5 D6 t4 Q
return FLogin;
4 V; l m9 {9 k& \7 }$ n( I}
+ u, n0 I9 N$ u8 ~easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
) O( T: N4 r0 ]2 x cdspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
8 U' C' O6 Y* e- d' J, B9 ?. {7 B+ q & E, k& g3 z, u/ X' X$ I, D
比如客户端可以用如下代码 9 E" \3 j C* |1 Q
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer);
1 v& N) e6 Q: }" f" T3 K: Z j( P ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer;
. R4 v; h! V/ a# a0 n) x dispLoginServer.Login(); ) ?3 C3 n8 J5 a8 u- b
DM->cdsAnimals->Open(); / I7 v+ N, m5 n' [$ ~, e5 H- z# m
实现注册,并且打开相应数据集……
0 V* K7 y9 ]! W / {6 d" o3 s) O7 T7 g
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 $ J Z# c- v9 R8 B/ r6 s$ c# M. x2 R. n8 r
绝大多数程序应该足以…… |