随笔 - 6, 文章 - 1, 评论 - 135, 引用 - 0
数据加载中……

急!急!!急!!!求解如何在WebService中使用Session或Cookie---实现WebService身份验证(客户端是Flex)

     现在我们开发一个项目,业务逻辑层全部用WebService,客户端有Flex和Asp.Net,Flex和Asp.Net的所有业务逻辑数据库的访问全部调用WebService中的方法(好象Flex也不能直接对数据库进行访问,所以也只能用WebService),现在就出现问题了,就是WebService中的权限问题了,比如:我在WebService中有一个Login的方法,还有一个ChangePwd修改用户密码的方法,可之,如果我要调用ChangePwd方法前我一定要判断用户是否Login了,所以一般的情况就是在Login方法里面当用户成功登陆后就Sesssion用户ID或者是用Cookie来保存以唯一标识用户,然后我再在ChangePwd方法里面先判断访问的用户是否存在Session或是有Cookie标识,如果有的话我再允许修改修改他自己当前登陆的密码,但是这些当做为 WebService方法的提供的时候就出现了问题,我在访问用户成功Login后Session当前用户的ID之后,当用户再去调用 ChangePwd方法的时候Session却丢失了,原因是我并不是直接打开IE访问WebService页面,然后在点调用那一个 WebService方法,那样我能成功验证。但我现在要是在我的项目中添加WebService引用,引用成功后.net便会自动生成一个App_WebReferences文件夹来保存引用的WebService的WSDL等信息,我们在调用的时候将WebService实例化后就直接调用Login或ChangePwd方法了,但是我是这样调用的话当我成功Login后能Session一个用户ID,但是当我在调用 ChangePwd后却不能成功,因为我在ChangePwd是判断了Session是否存在,结果Session不存在了,我明明在成功Login后 Session了用户ID但是却在第二次调用ChangePwd的时候不存在了,郁闷…我用Coookie亦如此,也丢失了,测试可知,我打开页面直接访问WebService中调用他的方法能成功,能植入Cookie,但我添加WebService引用到项目在调用Login方法时候根本就没法值入Cookie,没有报错,但是我在网页临时文件夹和Cookie的文件夹内都找不到Cookie,第二次在调用 ChangePwd时我在判断身份时候当然也是个Cookie不存在。

     在网上看了很多Cookie和Session的东西,在WebService中能应用的没几个,有人说要在客户端写个 CookieContainer的Cookie容器来保存WebService传回来的一些Cookie那样便不会丢失,但是有一个非常严重的问题就是我的客户端还必须是Flex,如果只是在.Net中调用那当然没事,我在.net中就直接判断Session或Cookie很方便的,也不存在丢失的问题,就根本不需要的在WebService里面去判断权限了,但是我WebService的客户端还必须是Flex,Flex它好象不能做权限处理的,数据库也不能直接访问,所以便要调用WebService,所有我便想把权限处理放在WebService里面, 客户端不用管的,所以我就不希望还在客户端去写类似:System.Net.CookieContainer cookieContainer = new System.Net.CookieContainer(); 然后在MyService.WebService service = new MyService.WebService();       service.CookieContainer = cookieContainer;为我的客户端不只是.net,还有Flex,甚至其他的,那它能有那个对象吗? 所以我就希望能就直接调用,我在WebService里面验证,验证通过就执行,验证不通过则不执行,但是当我想当然就这样做的时候,就出现了以上述的问题,我自认是菜鸟一个,想不到其他办法,现在又真的很急,所以请大家都帮帮忙,给我出出注意,请原谅我占了主页。

      补充一下:基本上我可以确认业务逻辑一定要写成WebService,因为我们经理说不止Flex下次我们的OA系统可能也要用WebServce,WebService能通用,所以就只能在WebService中写验证即用Session或Cookie验证了,请大家在这里帮我想想办法解决。谢谢!!!(如果大家愿意帮我忙话要测试的话可以留言问我要测试WebService,我已经把我写的几个WebService挂到了网上) 感激涕临!!!

posted on 2008-06-02 11:10 Jonllen Peng 阅读(2451) 评论(29)  编辑 收藏

评论

#1楼    回复  引用  查看    

webservice上加上属性
[WebMethod(EnableSession=true)]
public void MethodName()
{}
里面就可以直接用session了。
2008-06-02 11:15 | 第一控制.NET      

#2楼 [楼主]   回复  引用  查看    

@第一控制.NET
我在每个要用Session的WebMethod方法里面都已加了EnableSession=true,不然都会用不了Session而且会报错,现在不是这个问题了,仍然谢谢!
2008-06-02 11:20 | Jonllen Peng      

#3楼    回复  引用  查看    

看来你得把权限管理系统做成一个WebService,登录成功后,你要把某一验证成功的标识信息传到客户端(aspx,Flex),在客户端保存这一标识后,每次调用后台webservice function时,作为参数传进去,我不知道.net是否可以做aop,如果可以,就可以拦截每一个function,进行权限判断了。
2008-06-02 11:22 | 电视机9号      

#4楼 [楼主]   回复  引用  查看    

下面我贴一段已有朋友帮我想出的解决方案及其问题

--引用--------------------------------------------------
killkill: 写这方面的经验比较少,但是能不能要求Flex每次发送请求的时候在参数里面附加一个SessionID呢,而SessionID由你的程序产生,例如GUID,基本原理就是你自己重新实现一个Session。
--------------------------------------------------------


--引用--------------------------------------------------
Jonllen Peng: @killkill
这个我曾经考虑过,你的意思是不是当我成功Login后传回到客户端一个参数,这个参数就是自己产生的一个SessionID,然后当我再调用ChangePwd方法的时候把这个SessionID传回来,就能够标识了,你是不是这个意思?但是我WebService里面有好多方法,用到增、删、改和后台基本上都要用到权限判断,那我乞不是在调用的每个方法里面都要加一个保存SessionID的参数?
请问你知道Flex会不会接收到Asp.Net中产生的Session对象,如果能的话当前还可以解决问题,好象可以用,不过Flex好象产生的swf文件和as脚本都在客户端生成,无所谓服务器,那Session怎么用?但是最好还是能在WebService中做权限验证为好,不然那别人调用我的WebService就直接操作那还有什么安全可言。
--------------------------------------------------------


--引用--------------------------------------------------
killkill: 所谓Session的实现,据我见过的WebAPP都是由客户端回传一个SessionID的,这个SessionID存在于Cookie中,并不会附带任何数据,当然不会有非常“强大”的Session对象了。
Session的数据是存在于服务器端,客户端仅仅保存一个SessionID。
如果通过抓包分析的话,即使原始的Cookie纪录的SessionID也是可以得到,并可被第三方窃取的,所以我个人认为,手动实现Session和自动Session的安全性应该是同级的。
手动实现的副作用就是你每个WS方法都要多个参数了。
我的方案是这样的,在服务器端维护一个这样的数据结构
假设SessionID为GUID
Dictionary<Guid,Dictionary<string,object>> Sessions
登陆后 Sessions.Add<SessionID,new Dictionary<string,object>())
判断是否登陆就 Sessions.ContainsKey(SessionID);
用某个Session["key"]就这样 Sessions[SessionID]["key"]
当然Session的超时,LZ还是要想办法的 :)
虽然显得很笨拙,但是按照LZ描述的情况来看,笨拙点应该是可以接受的 :>
--------------------------------------------------------


--引用--------------------------------------------------
Jonllen Peng: @killkill
谢谢!非常感谢再次回帖,但是我觉得还是那个问题,如果要我在每个WebService方法里面多加一个保存SessionID的参数的话,感觉太麻烦了,如果我每个都加一个参数的话,那可能又会出现一些问题了,比如我Login成功后我是返回一个用户的Model的,登陆失败的话则返回NULL,成功就返回的Model里面包含了用户的一些字段信息,那我返回的SessionID那又如何传回?我个每一个WebService都有返回值的,难道我在参数里面多加一个ref或out类型的参数,那在WebService中又是怎么样的,目前还没有测试行不行,又如果用了参数返回了SessionID,那我客户端不也要申明一个全局变量来保存SessionID?麻烦!,而且我不知道new Dictionary<string,object>())你那样能不能实现持久?我的基本功不怎么杂实,如果我再次回调时候Dictionary里面还会有没有object,还没有测试过。
不过我好象想到了一个办法,就是说还是用Session保存对象,只不过在Web.Config文件内配置SessionState为一个保存在数据里面的模式,好象有这个中模式,不知道行不行?但是可能还是存在有同样的问题?
--------------------------------------------------------


2008-06-02 11:32 | Jonllen Peng      

#5楼    回复  引用    

没看懂LZ的意思,不过对于用户验证,我的做法是:
Login后,调用WebMethod时,都把用户信息集成在SoapHeader里,在WebMethod上加上属性:

[SoapHeader("header")]
public void MethodName()
{
//一些身份验证函数
}

2008-06-02 11:40 | swsd [未注册用户]

#6楼    回复  引用    

到目前为止,WebServices还可以说是一种无状态(stateless)的服务。

  所谓stateless就意味着不保存客户端服务调用者的任何信息。这是由WebServices的本质所决定的。WebServices在本质上是要为应用程序之间提供数据通讯的标准,为企业应用之间动态地提供大颗粒度的服务,所以WebServices并不适合于非常精细的基于会话的方法调用以及复杂的事务(transaction)处理之中。

  也许有人会对我这点提出异议!因为,现在有很多WebServices的产品(如WASD),不但可以保存session的信息,使服务成为有状态(stateful)的服务,而且还实现了remoteinterface,可以在WebServices的会话中传递远程对象的句柄,让客户端可以操纵递远程对象(详细信息请参考:XML数据中,可以互相传送任何数据,包括sessionID和transactionID,有了这些ID,从技术角度上说,实现有状态(stateful)的服务和事务处理并不复杂。但是,这样功能缺少标准的支持,当前版本的WSDL还无法表示这些复杂的服务。在企业内部,你可以任意使用这些特殊的功能,可以自己定义会话状态的交互协议,因为服务者和服务调用者之间的通讯都在你的控制之中;然而要将这些服务发布到Internet上,其他的应用程序是无法根据标准去识别这些特殊功能。

#7楼 [楼主]   回复  引用  查看    

@swsd
你的意思是不是SoapHeader头里面添加一个属性,用来保存用户的唯一标识,然后在再每次回发的时候再去判断SoapHeader头里面有没有这个属性?能不能给出一些具体的Code,比如我再成功Login后怎么样,Changepwd里面怎么判断?
2008-06-02 11:54 | Jonllen Peng      

#8楼    回复  引用    

我这个项目用的就是soapheader添加一个属性
[SoapHeader("myHeaderMemberVariable", Direction = SoapHeaderDirection.Out)]
public bool AppLogin( string username, string password )
2008-06-02 12:02 | ljmzxq [未注册用户]

#9楼    回复  引用    

例如,确定修改密码,在WebService里加上如上定义

public CheckUserSoapHeader header;

[WebMethod]
[SoapHeader("header")]
public void ChangePassword(String newpassword)
{
if (CheckUserStatus())
{
return false;
}
else
{
//这里修改密码
return trur;
}
}

CheckUserStatus定义如下:
private bool CheckUserStatus()
{
if (header == null)
{
return false;
}
else
{
String name=header.Name;
String password=header.Password;

//以下是确认用户代码

}
}

CheckUserSoapHeader定义如下:

public class CheckUserSoapHeader:SoapHeader
{
public String Name{get;set;}
public String Password{get;set;}

public CheckUserSoapHeader(String name,String password)
{
this.Name= name;
this.Password= password;
}
}

在Flex调用WebService时,先调用以下函数添加SoapHeader信息:

private function addHeader():void{
var q1:QName=new QName("http://temp/", "CheckUserSoapHeader");
var header:SOAPHeader=new SOAPHeader(q1, {Name:"test",Password:"111111"});

handler.clearHeaders();
handler.addHeader(header);
}


2008-06-02 12:07 | swsd [未注册用户]

#10楼 [楼主]   回复  引用  查看    

@swsd
我大概看懂了你什么意思,你就是把用户名和密码保存在SoapHeader,然后每次再回调的时候在根据传回的用户名和密码去判断权限,那我在Flex的客户端还不要每次把用户名和密码传回到服务器端了,而在服务器端我每次也要去验证,而且还不知道怎么去验证?是保存那里,还是每次都去查询数据库?感觉不怎么妥,不知道还有没有更僚瞥一点的方法呢?
谢谢你提出了使用SoapHeader的具体方案,考虑一下。
2008-06-02 12:37 | Jonllen Peng      

#11楼    回复  引用    

用silverlight吧...
2008-06-02 13:04 | A.Z! [未注册用户]

#12楼    回复  引用  查看    

既然能用FLEX 为什么不考虑SL?
从FLEX到.NET,这样跨越太大.受WEBSERVICES的限制,以后可能还会遇到意想不到的问题..


楼主你说用SessionState=SqlServer模式.我想这和Inproc是一样的吧,只不过是把会话数据从进程中转到了数据库中而已.你的会话还是会丢失.因为WEBSERVICES是无状态的.
按道理来说,你WEBSERVICES起用会话后,应该是可以了呀.莫非必须要在ASP.NET中,我想应该也是这样了.

所以你要手动处理Session,而且你在客户端要记录这个会话ID.
而且每次请求都要POST这个ID回来.
你可以写个基类,在WEBSERVICES基类中处理
不知道可不可以截取WEBSERVICES,扩展ISAPI?
2008-06-02 13:14 | 明年今天      

#13楼    回复  引用  查看    

SoapExtension扩展.希望对你有帮助
http://www.cnblogs.com/wobushixiaocai/archive/2008/04/11/1148196.html
2008-06-02 13:20 | 明年今天      

#14楼    回复  引用    

@swsd
谢谢,但是我试了你的那种方法,不行啊,抱错了啊,根本就得不到String name=header.Name;
String password=header.Password; 为空了。

#15楼    回复  引用  查看    

web service不像web,他是stateless的。使用SoapHeader是正常的解决方案
2008-06-02 15:52 | jillzhang      

#16楼    回复  引用  查看    

之前也碰到类似问题,在webservice写session不成功,后来换了种方法,把信息写入硬盘。关注中
2008-06-02 16:06 | Ryan Yu      

#17楼    回复  引用    

--引用--------------------------------------------------
jillzhang: web service不像web,他是stateless的。使用SoapHeader是正常的解决方案
--------------------------------------------------------
同意!
2008-06-02 17:09 | jerryleee1 [未注册用户]

#18楼    回复  引用    

class TransmitAdapter
{
private static string _jSessionID = string.Empty;
private static TransmitAdapter _instance;

/// <summary>
///
/// </summary>
private TransmitAdapter()
{
}

public static HttpWebResponse retrieveSessionID(HttpWebResponse webResponse)
{
char[] sep = { ';' };
String[] headers = webResponse.Headers["Set-Cookie"].Split(sep);
for (int i = 0; i <= headers.Length - 1; i++)
{
if (headers[i].StartsWith("JSESSIONID"))
{
sep[0] = '=';
_jSessionID = headers[i].Split(sep)[1];
break;
}
}
return webResponse;
}



public static TransmitAdapter Instance
{
get
{
if (_instance == null)
{
_instance = new TransmitAdapter();
}
return _instance;
}
}

public string SessionID
{
get { return _jSessionID; }
set { _jSessionID = value; }
}

}
2008-06-02 17:26 | sandao [未注册用户]

#19楼    回复  引用    

不知道能不能帮上你,我这个是用
c#的客户端调用java端的webservice,所以session是用jsessionId
2008-06-02 17:30 | sandao [未注册用户]

#20楼    回复  引用    

你可以抓取报文看看。在http头里面jsessionid每次是否一致
2008-06-02 17:31 | sandao [未注册用户]

#21楼    回复  引用    

用cookies应该可以获取吧?
2008-06-02 17:46 | oldmoon [未注册用户]

#22楼    回复  引用    

前段时间搞了一个C++调用WebService的东东,也是需要登录,遇到了和楼主类似的问题。C++类库中还没有CookieContainer,只好自己扩展了一个来保存。
其实问题主要在客户端,客户端如果不能保存cookies的话,服务器端怎么改都没用。
2008-06-02 21:03 | 化石 [未注册用户]

#23楼    回复  引用  查看    

http://www.cnblogs.com/rosanshao/archive/2007/10/14/923906.html

这儿能解决你的问题
2008-06-02 21:33 | rosanshao      

#24楼    回复  引用  查看    

比较标准的做法就是使用SoapHeader。
一般把用户的登入凭证使用MD5加密后,直接存储在SoapHeader中,如果你在服务器端使用Cache缓存管理,也可以分配临时会话ID(相当于SessionID),管理你对此客户的服务资源——主要是状态信息。

使用Enterlib的Security,Cache库,很容易实现这个方案。
2008-06-02 21:33 | 海天一鸥      

#25楼    回复  引用  查看    

cookie 当然是可以用的否则


使用方法和表单验证是一样的;

这种方式用 SoapHeader 有些多余;

asp.net ajax 也可以用webservice 就是 表单验证方式
也就是用的 cookie

[WebMethod(EnableSession=true)] 不加好像 cookie 也是好用的;

Flex 不是 flash 吗?这个我不太了解了
flash 应该自动使用浏览器 cookie 的吧?就算不使用也能通过和js通讯实现读写 cookie

有了 cookie 自然就有了 session 了!

接着使用标准的 asp.net 认证方式就可以了;和 page 一样,没认证的就会跳转到登录页,webserver 就是会出错了。



2008-06-03 00:16 | 曲滨*銘龘鶽      

#26楼    回复  引用  查看    

我觉得博主是否可以考虑实现System.Web.SessionState.IRequiresSessionState 接口?

MSDN对这个接口的描述是:Specifies that the target HTTP handler requires read and write access to session-state values. This is a marker interface and has no methods.

我建议是:
1. 让服务端的WebService实现IRequiresSessionState接口,这个接口没有任何方法,相当于只是一个标记。
2. 客户端把认证的Cookie和SessionId都发回到服务端。

这样应该就行了。
2008-06-03 09:28 | Beginor      

#27楼 [楼主]   回复  引用  查看    

谢谢,谢谢大家我的给我的意见,非常感谢!现在关于如何实现身份验证,初具皱形了,荐与网上一些代码写了很或类似的但是并不是完全准确,而且对于WebService的一些SoapHeader在用法,什么时候需要实例化,什么时候要指定Direction = SoapHeaderDirection.Out(或In)非常重要没有具体的事例,故帖出我现在写了的一些代码,经参考,已传到网上在Asp.Net和Flex客户端调用测试成功!

在WebService服务器端:
public HeaderContainer header;//申明一个SoapHeader
//登陆,成功之后暂返回用户实体里面的用户ID,然后在发送到客户端以标识用户,MemberItem为用户Model
[WebMethod(Description = "根据用户名密码返回用户详细信息,返回NULL则登陆失败!",EnableSession=true)]
[System.Xml.Serialization.XmlInclude(typeof(MemberItem))]
[SoapHeader("header", Direction = SoapHeaderDirection.Out)]
public MemberItem MemberLogin(string username, string password)
{
if (username.Equals(string.Empty) || password.Equals(string.Empty))
return null;
MemberItem item = MemberManager.MemberLogin(username, password);

header = new HeaderContainer();
if (item != null)
header.SessionID = item.Member_inner_code;
return item;
}
//修改密码,需要有才登陆后的唯一标识
[WebMethod(Description = "修改指定用户的密码,且要修改的用户为当前登陆的标识的用户.",EnableSession=true)]
[SoapHeader("header", Direction = SoapHeaderDirection.In)]
public bool MemberChangePwd(int memberID, string password)
{
if (password.Equals(string.Empty))
return false;

if (header != null)
{
if (header.SessionID == memberID)
{
MemberManager.MemberChangePwd(memberID, password);
return true;
}
else return false;
}
return false;
}

//在.Net的客户端的调用
MallMember m = new MallMember();//申明页面级全局WebService
//登陆
MemberItem item = m.MemberLogin("123", "123");
if (item != null)
{
Session["Member"] = m.HeaderContainerValue;//登陆成功在保存从服务器端发送过来的标识
}
else Response.Write("失败!");
//修改密码
MallMember.HeaderContainer header = (MallMember.HeaderContainer)Session["Member"];
m.HeaderContainerValue =header;//给SoapHead(里面包含有SessionID)赋值一起发送到服务器端去,服务器能接受并处理。
Response.Write(m.MemberChangePwd(123,"123"));

//Flex客户端调用
private var m:MallMember=new MallMember();//申明全局WebService
var h:HeaderContainer;//申明一个SoapHeader头
public function login():void{ m.addmemberLoginEventListener(loginHandler);
m.memberLogin('allen','aaaaaa');
}
//登陆判断,注意headers[0].content as HeaderContainer,因为他返回的数组,因为可能有多个SoapHeader,而我们就只用到一个而已
public function loginHandler(event:MemberLoginResultEvent):void{
if(event.result!=null){
h=event.headers[0].content as HeaderContainer;
Alert.show("登陆成功");
}
}
//为什么要传的是HeaderContainer0,而在去指定HeaderContainer,可能是因为它代理类的原因,添加WebService引用后自动生成的,
public function update():void{
if(h!=null){
var h0:HeaderContainer0=new HeaderContainer0();
h0.HeaderContainer=h;
m.addmemberChangePwdEventListener(updateHandler);
m.addmemberChangePwd_header(h0);
m.memberChangePwd(1,"mima");
}
}
更新事件
public function updateHandler(event:MemberChangePwdResultEvent):void{
if(event.result!=null){
Alert.show("更新成功"); }
}
//异常处理
public function fault(event:FaultEvent):void{ Alert.show("出现异常!"+event.fault.faultString);
}

现在大问题处理了,就是那个权限即SessionID的问题了,上述咱保存的是用户的编号(只为传递SoapHeader做测试),可知这样是绝对不安全的,如何别人知道你的用户ID了岂不就能直接操作?所以要产生一个想Session一样的唯一会话ID给用户来标识,起到和Session一样的效果,而每次在SoapHeader里面传递的就只是SessionID了,在者每次传递用户名和密码,当回发到服务器端时再去查询数据库去验证,但安全也是个问题,传递过程中用MD5加密?本来就很麻烦了再添麻烦?还有一中解决方案是在数据库里面建一个OnLine在线表,每个在线用户分配一个GUID唯一标识,再定隔一定时间更新即相当于会话超时时间,能起到Session同等的效果,但是感觉性能欠佳,故请大家给点意见,非常感谢!!!
2008-06-03 11:35 | Jonllen Peng      

#28楼    回复  引用  查看    

http://www.cnblogs.com/rosanshao/archive/2007/10/14/923906.html
这儿能解决你的问题 ,这儿有比较详细的解决方案,使用Server端使用Session,Client使用CookieConter,如果是Flex也是一样的,获取到Cookie后,提交到服务器。这样的解决,改动,扩展比较方便。

使用SoapHeader也可以,如果你的方法比较多,每个WebService都要去加一句判断,相当不方便。可以加个HttpMoudle,判断SoapHeader的用户名和密码,以及请求的URL来判断是古接受此请求。
2008-06-03 11:56 | rosanshao      

#29楼    回复  引用    

我记得SoapHeader在MSDN里面就有很详细的说明

另外,楼主提到客户端有Flex和Asp.Net,这个Asp.Net就是一个Web站点吧?是的话,楼主要注意下了,如果是Flex的话,应该是用户直接通过Flex调用WebService,那么当前用户的计算机就是实际的WebService客户端,但如果用户是访问某个Web站点,然后由这个Web应用程序再去访问WebService,那么此时对于WebService来说,实际上客户端只是这台Web站点的服务器,而不是这个Web站点的实际用户计算机,在进行身份验证的时候就需要注意下,需要在你的Asp.Net程序里面向WebService提供当前实际用户的身份凭证,个人觉得从这点看,Session就不是很合适了,SoapHeader反而可以比较方便的解决这个问题。
2008-06-03 15:56 | 古巴 [未注册用户]

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-06-02 11:18 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: