|
$ X% l* i! ~1 @; @9 L前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 " F2 A: k4 b+ V: u$ _
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
( e( \7 z, u. ?0 S3 X安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案
: H; Z$ h" r' \. B) E现就我以前对MIDAS编程的理解,做一个简要的介绍,
# \% O! ~" i, z. G) Q以后有时间精力再详细举例说明
j1 @5 s4 q8 Y9 F5 s$ h5 I- d' I! L - R: _) I, `+ u" ?# u. {5 ~- x; C* E
win2k中dcom的安全级别有7种,其中 $ C) r$ x7 N; y" f( t+ L+ S w
最低的“无”安全检查暂且不论 ) O4 D* [3 R& I, f
默认的“身份验证”服务方式几句话也说不清, 1 E% C! i# U3 V+ w$ x
先放到一边,看看剩下的5种 3 K. q- ~+ q, |8 d
9 Q. E0 c* R @' u: A7 Q1.连接,仅对初始连接进行安全检查
3 s, d. L- Q4 B : H2 p; L) S0 N; d+ h3 x$ S9 P
因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
4 t. G" s, h" N8 M0 l; r 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的
3 a( k+ s: ~! I" J: C 可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 5 i1 s: v% |" g( {
interface ILoginServer: IAppServer " C( W1 ?% L! ]& c% J, H( D
{
- Y6 g, @' @9 j5 j/ v: A% Q' V [ , ~! ~2 |5 Y! V
id(0x00000001) : F- j4 H0 k/ f" Q; H u
]
7 v+ q/ ~" k" A1 { HRESULT _stdcall Login( void );
" D- {; ^/ [" S0 ?$ M0 D [ : [7 m# U' J' l6 _
id(0x00000002)
! G% |4 S9 W3 E/ y ]
: | J- A- ]/ l- y( l2 u4 T HRESULT _stdcall Logout( void );
5 d x5 k) L& s- A5 _2 k};
, p2 h/ b/ V1 e; ]# ?: c6 L 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider ! x" L3 y1 L3 s5 F- A* N
的Exported属性关掉即可让客户端无法看到服务器端的provider
: s7 }8 a5 k' \% n/ N7 [ 因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
9 B$ X: i' J( f) o9 c 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 * e3 h5 X- \+ o- W0 ?2 ?
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
3 A0 t5 w0 s* K" k& m 都是通过相应的ProviderName来指定的,如
+ p' W" w' ?8 gvirtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in
; M& N( x# ]( W8 `: x1 ?t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
) T2 a+ C% N% Q$ K i" b. o% C2 Ft & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ;
2 c; O$ z- c& M9 @" [! J# ? 取得数据方法的第一个参数就是你需要操作的数据集名称……
) w A/ [& r: i9 ^: _ 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉,
+ m/ W! r& h% [* o* c( B6 _2 l+ Y U$ } 则客户端看不到任何Provider,也无法通过其ProviderName取得数据 ' t9 E) o6 J @) d! Z
例如我在服务器端的RDM类中加入 & O; K# W( K" B! ] M) @! D
class TLoginServer : public TCRemoteDataModule
* I* I6 B8 ~1 s{
6 @! K' [: B# H! ]! T) N... " K( t/ O4 k: D" Z
private: // User declarations
" K# E! a) ~0 j8 Y, p bool FLogin; 6 q) X/ i0 I4 `+ u4 o7 i* k3 o( P
void __fastcall SetLogin(bool value);
( U& ]; p* e% r+ H" M bool __fastcall IsLogin(OleVariant &OwnerData); , ]! |; p8 `1 k5 v
...
2 \, ?& [) W. p; D/ J: O__published: 7 U$ Z' @5 q4 N) z5 u: q
__property bool Login = { read = FLogin, write = SetLogin };
+ f9 l( A+ \- [}; 0 o {6 O8 I) U: @4 D
在RDM实现类中
1 y4 k; ~# R4 o/ n+ Rclass ATL_NO_VTABLE TLoginServerImpl: 。。。
% m& j8 S% V R' d- P9 W7 B{ 1 q& l0 d6 L7 N8 G1 v
// ILoginServer
2 u% @; k8 K Yprotected:
$ N! O! G4 K9 r0 ?- v, y- s4 f STDMETHOD(Login()); & E1 N: I( O) t
STDMETHOD(Logout());
" B8 \5 _& F% J; ?* ]}; 0 Y, [* ]* L. e$ T, m9 C
然后简单实现之
& E# I9 ~* \8 @- u__fastcall TLoginServer::TLoginServer(TComponent* Owner) ; j4 o& i- j L" O3 s- k
: TCRemoteDataModule(Owner), FLogin(false)
2 I7 p3 m4 v. Y4 Z{
( {9 ~) N" N K# Z$ k9 o |# B} * {5 b+ `7 g9 ^
//---------------------------------------------------------------------------
. E' y, b: O' Y+ h+ } Fvoid __fastcall TLoginServer::SetLogin(bool value)
8 t" Q1 Q i: Q8 B' q{ ; f8 p+ s8 Y4 U$ Q+ y6 L6 x, t
if(FLogin != value)
2 W6 c0 r; O0 z8 s# {9 I e {
! j7 v8 _ g2 I) P# G" @ FLogin = value; / X# J k9 I# @6 f% ?
dspAnimals->Exported = FLogin; 8 D3 m5 `+ `" K0 ]/ x
} - U3 t3 w# ]0 \+ N4 A% P
}
/ r) O" k, t$ V+ u$ [//---------------------------------------------------------------------------
+ [: m! @4 o5 `* D. a2 ]8 ?# l; WSTDMETHODIMP TLoginServerImpl: ogin()
& @6 C" i0 E: x- f4 E5 W( e* \{ X0 n, P3 x3 x7 T P
m_DataModule->Login = true; ! A2 v p+ q, ~3 e& B; Y. T
return S_OK; " N- d, T" ?% i! u2 C: \* ]9 A. m
} $ L' R# F; `7 v8 {( S9 P8 W
//--------------------------------------------------------------------------- + K8 u2 y" X' n$ ^
STDMETHODIMP TLoginServerImpl: ogout()
5 w1 v r9 N; f4 }( A{ * Y/ U+ f' B6 T- \
m_DataModule->Login = false; 8 M4 ]" {5 X$ K0 d! Y9 c+ O
return S_FALSE;
2 q9 ?: @, W* f* D0 x} z& h, _7 U/ N r. o ?- e' w
//---------------------------------------------------------------------------
9 J7 a& ~ t) Y( \3 A1 Gbool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) . H6 h/ L; }3 T2 ^5 B) Y4 j
{
# b* w9 Q$ g1 Q/ Q! ? return FLogin; ]6 I8 y2 K! p6 V% J
}
E( [6 ]0 a9 l1 ^easy 这样除非你的客户端成功调用Login方法,否则他是无法使用 q- Z, p0 m+ E
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同 * g7 E8 k) W1 v5 j: k
2 y: A2 {) Z2 r. J5 Y( Y比如客户端可以用如下代码 7 I2 G" A. X' w5 j# s
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer);
: P/ `3 I& K( U6 p* f8 z ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer; 0 e" c1 Q: O" \0 r, ^
dispLoginServer.Login();
1 b7 Z! F' D, n+ g u" s6 i: M DM->cdsAnimals->Open();
8 s$ l. i$ y& D8 d实现注册,并且打开相应数据集……
, _, c. l1 g0 |- L! v; G 1 o" a" h7 r; g P; D+ V
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对
. ]7 ^0 a, @3 a0 M) N: r绝大多数程序应该足以…… |