|
( |6 D5 n3 f7 F& S4 L' T! u, x
前面hawkfly提到的在多层编程中,服务器端安全性验证的问题
8 x# K# N8 }3 d, ]/ f在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的 9 E; e1 T* O& b+ h
安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案 1 J1 O( P% [2 x6 U8 \# m) i
现就我以前对MIDAS编程的理解,做一个简要的介绍, 6 T+ L1 z. [4 }
以后有时间精力再详细举例说明 4 V* ^+ C: Q) N, F
3 z: p; \+ z5 \0 R# S- {! Q8 w3 ~win2k中dcom的安全级别有7种,其中 & ^6 x6 L; D: _, t( |
最低的“无”安全检查暂且不论
7 C: \# s0 ~. C! ~& b2 h默认的“身份验证”服务方式几句话也说不清, 5 Y: a0 E+ m- F! e8 U
先放到一边,看看剩下的5种 % P+ `4 g4 S& m$ r4 M# g; _8 x- ^2 x
7 G# {# Q! k$ R _$ d% E
1.连接,仅对初始连接进行安全检查 0 W+ f1 p0 x% l& }! Q1 n
( i7 b2 g) m) c! [6 |* d 因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule / p/ w. ^) c/ K, [4 e8 l$ `
实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
! W4 T1 z J- P$ X- z 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 ( }( O' @& q$ F* G1 H
interface ILoginServer: IAppServer
8 F y. }( ^1 Y8 M' M; y{ 8 W8 o% q2 b, E* `" f# _. Y! `
[ 1 _0 p8 b$ k0 I( N
id(0x00000001)
# u# M) `5 `4 }; V# g8 m ]
( F- [8 B2 O6 G0 r9 F2 Y HRESULT _stdcall Login( void );
u1 t+ D: o2 K" q" S [ # C7 {3 v. t0 t3 Y0 F- P
id(0x00000002) * i6 a/ ^- l3 c3 J
] $ ^+ G0 {0 M$ d- L; a
HRESULT _stdcall Logout( void ); ; {. S' g; V" Q4 v4 `) A) `
}; 4 `2 W6 [$ z- b7 t
在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider ! M8 i" [7 X8 V4 E
的Exported属性关掉即可让客户端无法看到服务器端的provider
- s f2 {0 |1 e \7 D 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
% {0 H4 x9 ]; T 所有的Provider,如IAppServer::AS_GetProviderNames可以取得
- z1 p- b) S0 @8 M 服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
@* K0 H" P2 K- E8 N 都是通过相应的ProviderName来指定的,如
# [0 r0 O; ?3 N( ~virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in . s8 t: L5 J# G
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian 4 R8 r) u" y$ B' q0 g) ~$ [
t & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ; & y; O# j7 D, y0 }' H) W
取得数据方法的第一个参数就是你需要操作的数据集名称……
( ~ p% K f1 m- K( V( T 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
, z. l9 b1 o% @+ Q1 I0 S- K 则客户端看不到任何Provider,也无法通过其ProviderName取得数据 3 B5 g4 c; d+ H/ f9 D2 E( F
例如我在服务器端的RDM类中加入
, t9 ?3 ]9 l) n. z0 v" O" Mclass TLoginServer : public TCRemoteDataModule . d, H3 ~* g2 k( B, u" _
{ + Z; V! o7 X6 `' s% h
...
; _7 }- \& J' R% Bprivate: // User declarations
, n- K& V6 U$ t+ T6 r bool FLogin; ; Z5 c& t& Z+ l' S2 B
void __fastcall SetLogin(bool value); ) |7 c" T) T" `' o0 P! [7 \9 E* g
bool __fastcall IsLogin(OleVariant &OwnerData); + C+ o7 s8 o4 u
...
. |% j4 u2 q9 O# A__published:
' e& P8 i G- ]1 ? __property bool Login = { read = FLogin, write = SetLogin }; a' _" O' e# M) G
}; $ j' `, x) `5 m+ e( D
在RDM实现类中 " E' J5 u/ C9 l
class ATL_NO_VTABLE TLoginServerImpl: 。。。 3 Q1 b2 ^. k9 C8 {/ u5 U3 c
{
% o1 i1 t3 G. L% x! [5 L: G) B// ILoginServer
w) P( a" o/ K; |$ d1 ]' Gprotected: 7 R4 \: k' {9 X+ C! ~; t
STDMETHOD(Login());
+ [7 _! ]3 n& a/ N1 J9 X. Z STDMETHOD(Logout());
% t, ?6 k: g+ {}; 5 P) m' q; M1 v* |
然后简单实现之 ) }3 B# Q) T; z m- `
__fastcall TLoginServer::TLoginServer(TComponent* Owner)
, \# [7 B0 E* _; A3 B* ` : TCRemoteDataModule(Owner), FLogin(false) J( q$ \# O9 |- S
{
6 p' [! O4 w, M0 Y} 2 }& W% j; U& ]0 x, `
//---------------------------------------------------------------------------
2 M! Q# E/ `/ W Jvoid __fastcall TLoginServer::SetLogin(bool value)
; C9 z3 O6 m2 k" E G2 X1 Z% r{ 5 C0 j5 r4 |" u: b9 G5 r
if(FLogin != value) 5 T9 {2 x( g- X' ^) v# r+ L
{
8 h, Q0 ~. E& T% U+ Z FLogin = value;
: ~, @/ \1 \+ s" W5 b+ i. `% n dspAnimals->Exported = FLogin; , }9 y5 p* o# j: @6 d( e b
} . e5 p! T7 I- Q9 s% ~4 P S
} / o, Y3 r Y7 N. \8 U& t. u
//--------------------------------------------------------------------------- 9 P6 p4 b& ?, _: ]8 V9 \6 d7 [
STDMETHODIMP TLoginServerImpl: ogin()
( O' g9 C8 o" }+ n$ L{
+ I9 E# m" ` n3 c z- h R m_DataModule->Login = true; 7 P$ O, U3 o3 b: H
return S_OK; C! W7 [) h9 V$ t+ {9 b
} ( N- J: u- X& V; d: D- W9 a
//---------------------------------------------------------------------------
: {5 i7 S$ {0 \; g c8 Z. l: ]: d+ LSTDMETHODIMP TLoginServerImpl: ogout() 6 \0 d* s9 o" ^
{
% r! B- b" ?& x m_DataModule->Login = false;
/ O8 V0 S' U/ z7 B6 U8 a return S_FALSE; ! x- | ^! U) {3 ?: M5 Y) L% B
} / R8 e( K- ?$ A( s
//--------------------------------------------------------------------------- : N2 x1 H4 }. h& U. I n
bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) ; s' K2 K4 ~/ B" R
{
& s1 x5 ~ K" o% K return FLogin;
! P& H$ D+ O0 h( y, i/ {}
7 Z$ w9 C! n5 \4 D1 C9 R: h* Veasy 这样除非你的客户端成功调用Login方法,否则他是无法使用 4 I( X* @* P `" l) N( v/ j
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同
8 U. t, ~5 z+ j* {2 P$ w7 w
( C8 W. O/ b# f1 F; W+ h比如客户端可以用如下代码 4 t8 P6 C! B9 H. R. I. k, E; E, |( ]5 @
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer); j% W X4 E( i' @+ {
ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; & j# v! u/ l4 F" S+ C, V
dispLoginServer.Login();
' t# N, f+ H! Z+ u) \$ o5 @ DM->cdsAnimals->Open();
& L' g+ q5 @3 v' d实现注册,并且打开相应数据集……
) \8 q& u6 S5 { G5 [( x3 ?
+ x4 ~1 N% H1 ?/ [$ ~9 o因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 * f- Y+ i8 r$ L; B+ N2 T7 X
绝大多数程序应该足以…… |