|
; O( e% {( ?1 |% y% m6 o& a* e9 {前面hawkfly提到的在多层编程中,服务器端安全性验证的问题 1 R8 K! U# h+ ~2 B3 R0 X
在以socket方式实现MIDAS时尤为明显,DCOM/CORBA都有现成的
3 b) V, H& t& G( z# l) q5 U/ a1 T安全管理功能,而socket方式在这方面比较薄弱,没有现成的解决方案 2 }% y4 e) |$ l( f' X% q
现就我以前对MIDAS编程的理解,做一个简要的介绍,
* y+ N3 q# u5 n$ b以后有时间精力再详细举例说明
7 B* ~" Q2 A8 j* F : [3 W. F" f5 y1 z* w
win2k中dcom的安全级别有7种,其中
) b& G# @4 o& a. v$ d7 u8 g最低的“无”安全检查暂且不论 ' X+ a" q0 b( o6 }. L; l$ Z5 T
默认的“身份验证”服务方式几句话也说不清,
' X5 N. g, I1 s" O先放到一边,看看剩下的5种 ; w4 T5 D, f" p; [
2 H9 s, O9 }9 ^+ H1 V8 n
1.连接,仅对初始连接进行安全检查
) N x) |1 x: q# q
- F9 ]* W$ n$ v2 v6 } 因为tcp/ip是稳固的连接,因此每个连接,也就是每个RemoteDataModule
; o3 _4 _. b% C8 ~+ ?7 a- g 实例检测一次即可,实现方法很多,这里简要介绍一种最简单的 y& r& D( Z( R- w& |2 H3 m- t
可以在服务器端的TypeLibrary里面为你的服务器接口增加若干个方法,如 4 I. ?9 K3 |$ r* M$ }# D
interface ILoginServer: IAppServer
, q6 q: ?$ q# Q$ T5 L4 R! ^{ ; h& H9 G" k; |& v
[ 1 C7 o# e7 e. {- ]) B; _) J
id(0x00000001)
+ g( x, Q, J4 w7 |1 r: D1 g) V4 ~* h ] & {. G; r9 i7 ?2 X- h9 `
HRESULT _stdcall Login( void );
7 k" `3 u4 }! N) V/ |$ f [
6 ^* h& {% L l id(0x00000002) / s% e. ]) n' D) |: b
] , d" T8 q3 W8 {. O
HRESULT _stdcall Logout( void );
. ~8 `/ S- z) f};
0 |( l% r( H6 J 在方法中实现你的客户身份校验,而在校验之前,通过把RDM上所有的TDataSetProvider
( S9 b; f% V9 O 的Exported属性关掉即可让客户端无法看到服务器端的provider 0 J+ g3 v' S# d" B/ K% W1 S
因为客户端实际上是在连接服务器成功后通过服务器之IAppServer接口访问
' k+ }9 }0 {( m6 o$ b 所有的Provider,如IAppServer::AS_GetProviderNames可以取得 % O" H# Y7 ]. u7 L3 w
服务器端所有exported的IProvider接口名称,而所有数据的取得、修改
7 R7 [/ V+ M" u' n B. I 都是通过相应的ProviderName来指定的,如 ! x% |: k. d! D' ^$ b* z3 Z9 i: p
virtual HRESULT __safecall AS_GetRecords(const WideString: ProviderName, in + h% o8 P" I0 w9 B9 O3 M* E
t Count, int &RecsOut, int Options, const WideString: CommandText, OleVarian
' g9 V! d6 Z1 L Z0 O3 X- b Kt & arams, OleVariant: &OwnerData, OleVariant &GetRecords_result) = 0 ;
; f/ g! H) Y/ l' b 取得数据方法的第一个参数就是你需要操作的数据集名称……
- V4 ^$ u4 }) B7 A+ j 如果你把服务器RDM上所有的TDataSetProvider::Exported关掉, y$ t+ R" Y+ l% ]4 Z
则客户端看不到任何Provider,也无法通过其ProviderName取得数据 1 @! C4 H, K) L0 @. e
例如我在服务器端的RDM类中加入 & t8 f, X7 W+ ^' Y
class TLoginServer : public TCRemoteDataModule 7 E0 t$ p _7 R' k& x+ K- t+ T# y6 w
{
8 e4 h0 u$ v. Z$ Y...
/ j; s- j# t' K. _) M6 Q, Cprivate: // User declarations
0 v H8 i" V3 U% E/ b2 N( Z7 g! q bool FLogin; 9 T+ _3 [5 s. S; _
void __fastcall SetLogin(bool value); 3 ?$ d2 N8 Z6 \$ r
bool __fastcall IsLogin(OleVariant &OwnerData); , w" q: V4 |6 K" y6 w
...
2 r8 t" I. A0 o( U5 @ T2 D__published:
* q: ~9 R6 e& B __property bool Login = { read = FLogin, write = SetLogin }; " b- F7 \% U8 y8 u' I+ V
}; 7 T, B; Z" f: e( S
在RDM实现类中
4 O; [! Q+ e9 i+ G7 }class ATL_NO_VTABLE TLoginServerImpl: 。。。
6 A4 h( P3 L& F{ ! T: p: B; J! H
// ILoginServer / b3 w( ]1 G& v& y/ v9 x# c
protected: $ i" E. M# ?: X
STDMETHOD(Login());
h& [) D: e- J) [ STDMETHOD(Logout());
- D5 K: K* h& m5 H# |& k};
- |. [6 T! V$ H0 J% N/ y4 c然后简单实现之 - p2 d* `1 T/ q6 C
__fastcall TLoginServer::TLoginServer(TComponent* Owner) 5 L& @1 [, j* S+ _) m
: TCRemoteDataModule(Owner), FLogin(false)
$ p% `* A: i2 Q6 U6 m& C" f{
0 f. ^2 p+ _: ?& g3 m, ^}
. y/ k" V5 D% ?$ S. S2 T6 h//--------------------------------------------------------------------------- ; `" G8 _, n* J0 q$ \' v( g
void __fastcall TLoginServer::SetLogin(bool value)
" e4 O! l+ s) j& N9 f{ - T" y, q/ D: W) |6 ^
if(FLogin != value) x. `4 Q x. a# T
{ ; I5 H- ?$ S$ G s3 o' |8 Q% f+ Q/ O
FLogin = value; , m$ n; k0 ^/ f B) r2 A- l; R
dspAnimals->Exported = FLogin;
$ q; U+ j" x) `9 S8 A! ?4 x- x% c( g. ] }
% E( z. g5 S1 ~3 F4 Z ~}
9 H- ]; E2 `5 Y) D/ ^+ G$ L. j! D+ Z//---------------------------------------------------------------------------
9 i1 W0 ?) ^) M# B' cSTDMETHODIMP TLoginServerImpl: ogin()
. W2 i* W& G1 d' }2 L& i+ z{
+ B# j- H9 D9 I m_DataModule->Login = true; 8 Y1 b: }9 \6 b) d0 C J
return S_OK;
8 n8 A$ v1 @& J}
/ G4 m% k# v9 b q& Q. j//---------------------------------------------------------------------------
8 x M9 x `# P3 x: n* q/ ~5 [STDMETHODIMP TLoginServerImpl: ogout() 8 `. i+ f3 y4 ]3 j# ^. Z( n9 [
{
- Z1 [. l j4 n* B Y m_DataModule->Login = false; ( _' C6 ~$ R. ^: {
return S_FALSE; # Q' C. @( |/ L( X- S+ ?% U
} 0 y1 M9 A$ b2 q
//---------------------------------------------------------------------------
$ |: j8 u* w. \bool __fastcall TLoginServer::IsLogin(OleVariant &OwnerData) 0 A/ \1 R. ~& H! p
{
' Q3 \% s! @7 y: h4 I1 G return FLogin;
* b$ ?# G; |! p& l( R} / u) Z7 I4 w9 @0 V, x! P
easy 这样除非你的客户端成功调用Login方法,否则他是无法使用 9 `+ s+ C) t4 {7 N
dspAnimals这个数据集Provider的,成功调用后手工打开的数据集使用完全相同 ) N! U1 Q; T6 h: o A& R/ X
1 ?# _9 l. |+ ?. t* Z- t a比如客户端可以用如下代码 7 J) Q& ]7 U. \9 f# V# K% Q
IDispatch *dispAppServer = (IDispatch*)(DM->Connection->AppServer);
6 G0 S/ x2 A' Q! U ILoginServerDisp dispLoginServer = (ILoginServer *)dispAppServer;
$ @" u/ n4 y: a' ]' f& I3 W( q" i dispLoginServer.Login(); ; J7 J8 X- [9 D% @' P0 z" f9 `
DM->cdsAnimals->Open();
5 p! G& i$ o2 j8 c" \. q, v实现注册,并且打开相应数据集……
3 M& Q$ G2 I- `7 z3 s. n/ Q3 L 1 u9 a# c% g# k* t- u! o
因为大多数的三层程序对安全性要求并不高,因此这样的安全检测对 : p( w8 ^" a' i) L& [/ c" {, |
绝大多数程序应该足以…… |