!!!!!WebService与Cookie!!!!! (100分)

  • 主题发起人 powersite
  • 开始时间
高手呢?
强烈呼吁,,,
 
你可以看看下面的鏈接,也許有點幫助
http://www-900.ibm.com/developerWorks/cn/dmdd/library/techarticles/0212swart1/0212swart1.shtml
http://www.yesky.com/SoftChannel/72342371928440832/20030928/1732918.shtml
http://www.zdnet.com.cn/developer/code/story/0,2000081534,20026946,00.htm
 
谢谢springson,
我看了那三篇文章,
唉,
网上这些文章,
都写的很浅,
稍微一点深度都没有,
个人感觉,
网上的DELPHI方面的WEBSERVICE介绍,
往往都停留在肤浅的怎样入门的知识点上,
不知是不是可悲之处
 
说一个简单的方法,我们是这样用的,在重要的方法上加上用户名和 口令这两个参数
 
其實那些都是入門級的,不過一些簡單的用法倒是有了,你可以試試應用你的經驗,加入很多過程和函數,然後調用就行了。其實很多的例子都是從hello world開始的,我們只是做補充,如果我們把過程和函數放入dll或包,那是動態的調用,或者放到別的電腦,那是三層,如果放到activex中,那是com。說穿了寫法都沒什麼的。
 
quantum99:
是啊,
我开始也是这样想的,
后来考虑到作为服务端而言,每次方法调用,都要提供用户名和密码,而不是临时有效的KEY,这样的话,安全性毕竟不是太好,另外,每次验证都去读取数据库,恐怕性能上会打折扣。
不过,后来查悉GOOGLE提供给大家的WEBSERVICE也是每次都用用户名和密码,呵呵,
咳,实在不行,就只能这样了,
原本想用COOKIE,主要是看了李维的书,什么<<Delphi6/Kylix2 soap/web service程序设计篇>>,里面讲到SOAP的安全问题时,说是可以将COOKIE存放入SOAP的HEADER中,但只是轻轻带过,不作稍解,咳,
 
springson:
说得很有道理,对了,你的文字怎么总是繁体字的,难道??
 
我是在台資企業工作,我不是台灣人。只是操作系統是繁體的。
 
springson:
哈哈,原来如此,台企,那不错啊,
做哪方面的?
呵呵,有空大家探讨一下啊,
 
台資企業不是很好,只是有點像學校一樣。
有一幫兄弟是做java的,我是維護mrp(用foxpro寫的),數據庫老出一些最低級的錯誤,該刪除的不刪除,所以很麻煩,老闆又不肯換。
 
springson:
同感,我给以前公司做的产品管理就是用foxpro做的,经常莫名其妙出现重复记录,字段内容颠倒,好象正常启动正常关闭就没什么问题,可是那帮使用的人,就不清楚是怎么用的了,老出错。

powersite:
不知道你的webservice要求什么样,偶也没做过这样的,觉得一些东西可能对你有参考,就来说说。我用php做网站,需要登录后使用,开始是用php提供的session,有次服务器出问题,有2、3天session无法使用,网站升级的时候就设计了自己的验证方法(每个帐号都不能同时使用,ASP、PHP的session则没有此限制),使用登录时Cookie记录一个ID,把登录的信息保存到数据库中的一个表里,设定有效时间,现在一直在用,还是会出现问题,有时候无法登录,需要正常退出(清空Cookie)才不会出现问题。
http连接是无状态连接,必须要Cookie记录某个状态才可以,否则每一次请求都要输入密码了。我看了ASP、PHP的session,都是用Cookie记录一个ID(唯一的)。
清除登录用户列表可以在程序中加代码呀,设定有效时间就是了。
 
skadon:
我的使用是这样的,因为当前一个项目,全部都使用DELPHI进行开发,
因为要远程存取数据,最终考虑用WEBSERVICE进行远程调用,以获取数据,为了统一性,(里面用了CLIENTDATASET作为数据的载体),索性将WEBSERVICE也用DELPHI开发。
当然要考虑到“何人”能调用WEBSERVICE,所以想要进行验证,可以每次调用时同时提供用户名和密码,到数据库去核对是否允许,但那样似乎累赘了一点。
所以,想要一旦第一次调用LOGIN方法(也是WEBSERVERICE的一个远程方法调用)。登录后,返回一个KEY,而后,客户只要凭这个KEY,才可以进行调用,当然,这个KEY是有有效时间的,在一定时间后,该KEY将会失效,要求重新LOGIN一下,
 
skadon:
谢谢你,我的疑问是,普通的DELPHI,如果不用什么RESPONSE,REQUEST,如何直接进行COOKIE操作?因为,WEBSERVICE中,好像定义的函数和过程,可以是看上去很“纯粹”的。在设计时,不用牵涉到什么“RESPONSE,REQUEST”啊,,
 
我看到一篇关于登录身份验证的文章,做一个login.dll然后传递参数,不知对你有没有帮助.如果想要我给你贴上去看看.
 
夏日的落叶:
好啊,不管是不是对口,大家多了解一种方法也好,
先说谢谢了!
 
问题的提出:
我们正在开发一个较大的工程,为了分工也为了模块清晰,工程分了多个模块。但整个工程是需要登录来确认身份,并且根据身份确定登录这的权限。当然,不能为每个模块都开发一个登录界面,我们希望通过一个程序控制所有程序。

思路:
建立一个login.dll,它来实现登录功能,并且它提供一个界面,该界面能够连接到各个模块,当然,参数也传递过去。而各个模块只有得到正确的参数(userid和password)才能正确执行。
问题:
1、参数如何传递
2、参数如何不在客户端出现
3、各个模块如何调试

一、建立login.dll

一.1 ServerController内容:
unit ServerController;
{PUBDIST}

interface

uses
Classes,
DatamoduleUnit,
IWServerControllerBase, IWAppForm, IWApplication,
SysUtils;

type
TIWServerController = class(TIWServerControllerBase)
procedure IWServerControllerBaseNewSession(ASession: TIWApplication;
var VMainForm: TIWAppForm);
procedure IWServerControllerBaseCreate(Sender: TObject);
private
public
end;


TUserSession = class(TComponent)
public
datamodule1:Tdatamodule1;
user_id,user_name:string;

constructor Create(AOwner: TComponent); override;
destructor destroy; override;

function check_user(u_id,u_pwd:string):boolean;

end;

function UserSession: TUserSession;
//自定义的几个函数和过程
function cut_it(s:string):string;
procedure movetoDLL(DLLName:string;frm:TIWAppForm);
procedure movetoDLLAnonym(DLLName:string;frm:TIWAppForm);
function DLL2longURL(DLLName:string):string;
function DLL2longURLWithUser(DLLName:string):string;

implementation
{$R *.dfm}

uses
IWInit,Registry,StrUtils,login_mainIWU;
function DLL2longURL(DLLName:string):string;
begin
result:='http://'+RWebApplication.Request.Host+'/'+cut_it(RWebApplication.URLBase)+DLLName;
end;
function DLL2longURLWithUser(DLLName:string):string;
begin
result:=DLL2longURL(DLLName)+'?userid='+UserSession.user_id+'&password='+UserSession.user_pwd;
end;
function cut_it(s:string):string;
var i,j:integer;
begin
i:=pos('/',s);
while i>0 do begin
j:=i;
i:=posEx('/',s,i+1);
end;
result:=copy(s,1,j);
end;

procedure movetoDLL(DLLName:string;frm:TIWAppForm);
begin
frm.addtoinitproc('window.open("'+DLL2longURLWithUser(DLLName)+'","","");')
end;
procedure movetoDLLAnonym(DLLName:string;frm:TIWAppForm);
begin
frm.addtoinitproc('window.open("'+DLL2longURL(DLLName)+'","","");')
end;
function UserSession: TUserSession;
begin
Result := TUserSession(RWebApplication.Data);
end;

{ TUserSession }

constructor TUserSession.Create(AOwner: TComponent);
begin
inherited;
datamodule1:=Tdatamodule1.Create(AOwner);
end;
destructor TUserSession.destroy;
begin
inherited ;
end;
function TUserSession.check_user(u_id,u_pwd:string):boolean;
begin
result:=true;
end;

procedure TIWServerController.IWServerControllerBaseNewSession(
ASession: TIWApplication; var VMainForm: TIWAppForm);
begin
ASession.Data := TUserSession.Create(ASession);
end;

end.

这个单元没有特别需要说明的,其中的check_user是示意性质的,填上实际的代码即可。
这个单元里定义了几个全局函数和过程,以便其他地方调用。

一.2 登录窗口(单元)内容,略。

一.3 主窗口。当登录成功后,显示这个窗口,通过该窗口可以连接到其他模块。
这个窗口唯一要说明的是如何连接到其他模块,请看其中一个连接的代码:
procedure TFrmTop.IWLinkTestClick(Sender: TObject);
begin
movetodll('test.dll',RWebApplication.ActiveForm as TIWAPPForm)
end;
其中 movetodll在ServerController单元里已经定义过了。系统假设test.dll与login.dll放在同一个目录下。

二、建立其他模块
我们知道login.dll已经将参数传递过来,在其他模块里如何接收?看看其他模块的ServerController单元就明白了:
unit ServerController;
{PUBDIST}

interface

uses
Classes,
DatamoduleUnit,
IWServerControllerBase, IWAppForm, IWApplication,
SysUtils;
type

TIWServerController = class(TIWServerControllerBase)
procedure IWServerControllerBaseNewSession(ASession: TIWApplication;
var VMainForm: TIWAppForm);
procedure IWServerControllerBaseCreate(Sender: TObject);
private
public
end;


TUserSession = class(TComponent)
public
datamodule1:Tdatamodule1;
user_id,user_name:string;
//
constructor Create(AOwner: TComponent); override;
function check_user(u_id,u_pwd:string):boolean;
end;

function UserSession: TUserSession;

implementation
{$R *.dfm}

uses
IWInit,Registry,StrUtils;

constructor TUserSession.Create(AOwner: TComponent);
begin
inherited;
datamodule1:=Tdatamodule1.Create(AOwner);
end;

function TUserSession.check_user(u_id,u_pwd:string):boolean;
begin
result:=false;
//你的代码
result:=true;
end;

procedure TIWServerController.IWServerControllerBaseNewSession(
ASession: TIWApplication; var VMainForm: TIWAppForm);
var userid,pwd:string;
begin
ASession.Data := TUserSession.Create(ASession);
with ASession.RunParams do begin
userid:=Values['userid'];
pwd:=Values['password'] ;
end;
//userid:='0';pwd:='111111'; //请注意!!!调试时使用!!!

with usersession do
if (userid='') or(not check_user(userid,pwd)) then
ASession.Terminate('对不起,你没有这个权限。');
end;

end.
当创建一个新的session的时候,要判断是否有参数过来,如果没有参数,或者有参数但不被许可,那么“对不起,你没有这个权限。”。
本例中,每个模块都有自己的check_user,也许你不需要这样。
现在,每个模块都已经得到了传过来的口令和密码了,进一步的工作就是你自己的事情了。
但是,事情并没有就此结束,传递过来的参数会显示在客户端。这是不能接受的。我也在等待更好的解决办法,但在更好的解决办法出现之前,还用我原来的方法吧——这个方法我在别处说过:
在各个模块的主窗口的javascript属性里加上这样的代码:
var str=location.href;
pos=str.indexOf("?");
if (pos>=0) {
location=str.substring(0,pos);
}
可以看出,代码在客户端将参数截去了,这导致页面更新了一次,这也不算什么问题,因为仅仅多访问一次服务器而已。不过,客户端会跳出一个警告框,这很烦。去掉这个警告框很容易,只要将IWServerController的showResyncWarning置成false即可。
还要提醒一下:login.dll里使用的参数名称必须跟其他模块使用的参数名称一样,事先要约定好。

到这里应该说大功告成了。下面是“完美主义者”的讨论:
1、客户端真的看不到参数了吗?其实,既然带参数的url成功的访问了服务器,ie的下拉框里难免会留下痕迹。怎么办?两个思路:
A 你告诉浏览器:不要保存我的url。至于怎样“告诉”,我也不会!
B 口令加密,即使看到又如何?
2、其他模块如何调试?平时,我都是通过另一个产生exe的工程来调试,我想你也是。但程序需要参数,怎么办?看到上面代码中的注释了吗?这一行就是用来调试的。当然,你在浏览器中,输入url时手工添加参数也行。




to powersite :呵呵,这是别人的东东.你看有没有用.
顺便问一个问题.
我用intraweb application wizard 做一小程序.
运行时在IE页面有时会出现如下错误信息,如何解决,
No main form is defined.
在线候教.
 
夏日的落叶:
谢谢你!
呵呵,
intraweb闻名已久,
我倒还没有用过,
所以,
也不敢对您的问题妄发言,
望见谅,
 
powersite:
你的问题其实是一个比较经典的问题,就是一个中间件是有状态对象还是无状态对象的问题,WebService中实现的对象是无状态的,每一个接口的调用都会重新生成一个新的对象来响应。如果一定要保存状态信息,个人认为只有几种办法:
1. 用文件(包括本地数据库),把用户状态写到文件中,用户状态信息靠一个唯一标识来索引,每个接口的调用都可能要把信息从文件中读取出来,操作完毕后可能要再度保存到文件中;这种方法在客户端并发数小于10的时候是可以使用的;只需要定时把文件中的过时信息清理掉即可;
2. 用中大型数据库,原理和1中一样,处理的并发客户端数可以大大提升,但是如果客户端很多,而数据库不在本机上,可能会因为网络阻塞而大大影响处理效率;
3. 自己写内存管理过程,不保存到其他介质上,所有用户信息都存在全局内存中;定期清理内存中过期用户信息;这种方法虽然实现起来有一定的麻烦,但是却很有效,只要你的查找、删除、修改的速度足够快;支持多少人同时在线取决于内存容量,并发处理速度取决于你的程序和网络速度以及你的机器速度。我用这种方法的测试结果速度不比使用本机Oracle数据库存储用户信息慢,甚至更为稳定,使用异地数据库在冲击人数比较多的时候还容易出错;
其实无外就是表现方式不同而已,原理都大同小异,想偷懒又想得到好的性能,目前看来比较不可能;如果你客户端不多,可以使用ini文件存储,很简单,速度也很快,只是同时多人冲击就会导致I/O瓶颈;
个人认为自己写内存管理比较有效。
 
谢谢YuanLiang的分析,
我目前也是用类似于INI方式,
我现用XML文件存储着登录“状态”信息,
存放于本机上,
因为目前是在测试阶段,
连接数极少,
所以暂时正常,
但我也认为,如果有大量的用户访问,
XML文件必将成为访问的瓶颈。
所以,正想将该状态存入到数据库中,
让数据库去控制。呵呵!
但是你所说的第三点,
存入于内存中,
请详细说一下好吗?
我主要对于“定时”去处理比较难以解决,
因为,
DELPHI中,
WEBSERVICE端的TTIMER定时器是无法正常工作的,
谢谢!
 
使用内存管理其实是在内存中维护一个用户信息列表作为全局变量,至于这个列表你究竟使用何种方式实现以及效果,那就要看你的编程水平了,例如:
1. 你可以使用一个静态数组依次保存用户信息;当用户数目超过数组长度后,将时间最久的一个用户信息(数组的第一个元素就是时间最久的一个)保存到文件或者数据库中;新的信息填写到相应的位置上;这种方法要求你的用户信息索引值连续,这样会引起一个用户窜改自己的索引编号假冒起他人的问题;如果安全要求不要,可以考虑这种方法;如果有一个请求你无法在数组中找到用户,则需要从你的辅助存储介质中找用户信息;这样你就不用在WebService中定期清理内存,只需要一个另外程序定期清理你的辅助存储介质即可;
2. 你也可以使用hash链表的形式存储,不过就不能象数组那样简单根据一个下标索引判断时间最久的一个用户,需要你在webService中另外定一个全局的线程,在一定的时间或者是时间间隔内对内存进行清理操作(例如在一个一般不会有人使用的时间进行清理,半夜3,4点?呵呵);这种方法可以满足安全要求比较高的情况,用户的索引值可以完全没有规律,无法假冒;
我说的不见得就是好的方法,还有其他形式来实现内存管理,取决于你的信息内容,数据类型,信息量等等,以及查找、添加、删除、更新的速度,应用场合,服务器配置等等,你的程序是否会因为你写的这些内存管理而导致内存碎片的问题也要考虑……
总之这种方法可以满足比较高的要求,但对你的程序编写水平要求也相对高一些;
 
顶部