|
3 R5 l* w8 `2 Y1 n; m V
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 $ I3 T2 `; G S1 y' a
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 , x- ~1 |+ L% V+ g& F
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案 % Y$ A' B# E4 P' r: p
现就我以前对MIDAS编程的理解,做一个简要的介绍, ! Y8 B/ Z3 K$ C, C
以后有时间精力再详细举例说明 # p; J. R- E, T
5 x4 @+ u! @8 |& [1 R& V; Swin2k中dcom的安全级别有7种,其中
- f: z# B+ I' V3 O4 R最低的“无”安全检查暂且不论
7 Z$ F# x6 J4 |& q4 s默认的“身份验证”服务方式几句话也说不清,
+ v- B% a; l) M" K8 [先放到一边,看看剩下的5种
* {5 ]7 b7 i7 O d3 w f & d2 b* U5 k/ x# M+ |" G
1.连接,仅对初始连接进行安全检查 & F# B% s+ f# Y. a8 k: T
( p2 [. a3 s5 K# @: B# `
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule 6 z* U Q: ~( d$ g+ s8 a
实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
7 w3 e' M4 M4 d. h# l 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 # o% r6 [) v: w; s& K9 }* X
interface ILoginServer: IAppServer 2 M8 z6 y w( s' C5 p3 _" ^8 M
{
( J( M2 G9 N, D% i; {5 R1 B. G [ H( @1 X8 p! w
id(0x00000001) . _' W ^+ u3 _3 l
] m$ Z( M9 x, i/ ]9 e
HRESULT _stdcall Login( void ); . u% X. I: u2 r' o, i
[ $ e: q4 w6 i: ?& s5 T: a' W4 l
id(0x00000002)
% K+ Y* T% g* k6 V" g9 g7 i7 U ]
/ L9 J* v% Q- k* T. R! h* f) M HRESULT _stdcall Logout( void );
6 Z: D8 A, v* A/ G. S: g0 b) x}; & m: y' j" l: C5 g9 H$ K2 s) [
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider & v& _" _* k: T& i
的Exported属性关掉即可让客户端无法看到服务器端的provider
9 v) H* I b, {- g 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
1 K/ Y& i$ t4 q- N% { 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 3 D( Y, P- d4 f+ q% i
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
7 h( S- y" _3 _ B l) t 都是通过相应的ProviderName来指定的,如 3 ]9 [. H6 [: k+ P% y I
virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in
1 [( o h; x5 n! S+ ^" tt Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
$ F& Y# n- d! H/ x& g) ht & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ;
3 V$ `9 B7 O# u1 y 取得数据方法的第一个参数就是你需要操作的数据集名称…… 3 k+ p6 F7 M* _
如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, - H) a3 S E' B; N- k2 F6 q' }
则客户端看不到任何Provider,也无法通过其ProviderName取得数据
" v y% H$ u- P% i 例如我在服务器端的RDM类中加入 - R$ u9 @& u: ]
class TLoginServer : public TCRemoteDataModule
+ t' `+ B5 `9 e- n1 n- I4 h) U{
; K6 I1 `- v3 i... ' Z9 U' Y7 e# q, H" i5 L
private: // User declarations
, ^1 r q6 w) V$ A bool FLogin; 4 s, T4 e2 I6 ?- Q0 @
void __fastcall SetLogin(bool value);
% a; Z6 ~- J4 w6 P' |8 ^8 { bool __fastcall IsLogin(OleVariant &OwnerData); . t) e& ]/ ~9 D# }5 m' C# x8 I4 h, A
... 5 `5 R6 I2 a3 u5 G$ x% N' _
__published:
) q6 {5 I7 _. V __property bool Login = { read = FLogin, write = SetLogin }; * C' R3 w. P3 [+ x; W
};
o+ [0 ~0 G& i. Z2 r/ @* a+ Y) Z在RDM实现类中 ( V. v9 x' q; }3 {6 J3 I$ N4 c
class ATL_NO_VTABLE TLoginServerImpl: 。。。
0 D; R. D* f7 V" e* @5 `{
7 F3 e: o! c6 k$ p# j- }% M4 }// ILoginServer
/ w$ p7 X! Q$ M! Mprotected: 5 k7 G' I3 t! _
STDMETHOD(Login());
( g; h* k& f3 S. t, F% c: v% Q6 u STDMETHOD(Logout());
; g6 i$ y+ Y. w7 }}; - D( c! D5 D: L7 W
然后简单实现之
& |' y9 V4 ]; b3 c$ C, O+ Z__fastcall TLoginServer::TLoginServer(TComponent* Owner) ( s( F/ b( J- v G
: TCRemoteDataModule(Owner), FLogin(false)
+ s! M& Y2 i6 r) C5 Y$ G7 S! V+ ^{
: q0 j7 w6 j1 n, J$ y; @} " n2 |" b+ e- F: H" T, k1 v
//---------------------------------------------------------------------------
8 M8 {/ s' k3 j3 F' R" j8 fvoid __fastcall TLoginServer::SetLogin(bool value)
$ ?' m. c* s- v1 n' X. h{ " c8 i! W4 C8 D2 z0 L+ {
if(FLogin != value)
! t8 B8 k/ c" u- _5 F) q/ h( I { 7 c9 e; |, q& u- c$ Y9 O
FLogin = value;
( D, f9 A/ Y4 X+ E3 ? dspAnimals->Exported = FLogin; 5 H) W& ~ Q1 x2 a8 z
}
+ i0 S+ \5 [# _+ X! i: U' W}
0 P) V! [. g& }& t//---------------------------------------------------------------------------
9 l- B8 z& X& b6 f# p, gSTDMETHODIMP TLoginServerImpl: ogin()
+ l* w6 I$ R: G# [9 _{
# ?3 x1 W$ ~( B: Y V, i7 f m_DataModule->Login = true;
" s6 E8 c) C, [$ j8 F return S_OK; ( s# B& F. C8 ~. D
} $ [9 i4 s: n+ a% ~' Z7 ~) B
//--------------------------------------------------------------------------- + M4 T0 B$ K! P3 `# C2 O2 z2 v
STDMETHODIMP TLoginServerImpl: ogout()
% Q/ P6 Y0 F9 }( m& `% b{
" u" E) e/ R7 } m_DataModule->Login = false;
& W1 p q z1 U1 ~( y" L return S_FALSE;
# u: U6 F' C+ P8 r5 J0 u}
# [1 |5 w* u7 b& K% ?" c//--------------------------------------------------------------------------- , O2 u* I2 |1 k5 I1 M' L; A$ Q, [! ]1 N
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData)
7 u3 ?( I3 ` Q- m2 A{ 3 t6 x2 d: z$ N; M
return FLogin;
1 x( ~7 _9 U+ U0 d5 x7 s# t}
o! n- I5 \3 z$ U, s0 }7 e/ [easy 这样除非你的客户端成功调用Login方法,否则他是无法使用
5 Z" Q( f4 X# \dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
4 y* I2 h; t+ i' `6 N$ G& e5 I
9 E" S* x* j7 [, |* V' j比如客户端可以用如下代码 ; e* f3 G3 d& n$ `/ d
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer);
8 x; s. b% k' u0 Q ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer;
1 q! C, c) H, c& | dispLoginServer.Login();
# G& }" }5 n" `: {! k DM->cdsAnimals->Open();
4 X {, x1 F- ]) C实现注册,并且打开相应数据集……
, {* U# g- F7 M; E
. R& X, |7 G& t) R' s& w2 T7 i# F3 c因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
2 n6 X' ]' g; g1 @3 X2 E9 M& s b% h绝大多数程序应该足以…… |