最近接触到的项目要使用.net调用xfire发布webservice,在网上找了很多方法,遇到一些“牛人”粘贴了千篇一律的代码,看着头痛,但实际的问题并没有解决,在一次调试过程中发现服务端的安全处理Handler是有状态的,于是想了个办法让服务器端保存客户端调用的认证信息,认证一次后并不再需要对身份进行多次认证
这里和大家分享一下具体的代码
关于使用Xfire开发Webservice大家可以去网上搜搜,都写的很好,图文并茂,
我在这里强调的是跨平台的安全调用和状态保留,本文将阐述如何使用SOAP Header传递认证信息并且保存客户端的调用状态
这是.net 生成代理类的使用wsdl.exe工具 配置文档如下
服务器上我们添加验证方法
import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.fault.XFireFault;
import org.codehaus.xfire.handler.AbstractHandler;
import org.codehaus.xfire.transport.http.XFireServletController;
import org.jdom.Element;
import org.jdom.Namespace;
public class AuthenticationHandler extends AbstractHandler implements Runnable {
public AuthenticationHandler() {
// 该线程检查用户登录是否超时
new Thread(this).start();
}
// 定义hashMap 存储用户验证信息,key经过验证的用户IP,或者客户端特定信息
HashMap<String, String> hash = new HashMap<String, String>();
// 当某个客户端登录成功后,纪录登录的时间,key 为经过验证的用户IP,或者客户端的特定信息
HashMap<String, Date> createHashDate = new HashMap<String, Date>();
private final static Namespace TOKEN_NS = Namespace
.getNamespace("demoService");
// 统计当前服务访问的人数
Integer count = 0;
public void invoke(MessageContext context) throws Exception {
// 假如hash中存在用户验证信息,则直接跳出该函数,不再进行验证
if (hash.containsKey(XFireServletController.getRequest()
.getRemoteAddr())) {
return;
}
// 得到客户soap header 对象
Element header = context.getInMessage().getHeader();
if (header == null) {
throw new XFireFault(
"Request must include company authentication token1.",
XFireFault.SENDER);
}
// 得到认证令牌
Element token = header.getChild("AuthenticationToken", TOKEN_NS);
if (token == null) {
throw new XFireFault("Request must include authentication token.",
XFireFault.SENDER);
}
Element name = token.getChild("name", TOKEN_NS);
Element password = token.getChild("password", TOKEN_NS);
if (name == null || password == null) {
throw new XFireFault("AuthenticationToken Error,name or password is null", XFireFault.SENDER);
}
String nameValue = name.getValue();
String passwordValue = password.getValue();
if (nameValue == null||passwordValue==null) {
throw new XFireFault("name or password's value is null.", XFireFault.SENDER);
}
try {
//进行服务器端验证
if (nameValue.equals("admin") && passwordValue.equals("admin")) {
System.out.println("登录成功!登录时间:" + new Date());
System.out.println("当前人数:" + (++count));
hash.put(XFireServletController.getRequest().getRemoteAddr(),
XFireServletController.getRequest().getRemoteAddr());
createHashDate.put(XFireServletController.getRequest()
.getRemoteAddr(), new Date());
} else {
System.out.println("fail login");
throw new Exception("Login fail");
}
} catch (Exception e) {
throw new XFireFault("Authentication Failed.", XFireFault.SENDER);
}
}
public void run() {
service();
}
public synchronized void service() {
while (true) {
try {
/**
* 取出用户登录时间与当前时间进行对比
* 如果当前时间与用户登录时间之差大于系统设置的某个时间,则将用户登录信息清除
* **/
for (Iterator<String> keys = hash.keySet().iterator(); keys.hasNext();) {
Date now = new Date();
String key = keys.next();
long time = now.getTime()
- createHashDate.get(key).getTime();
if (time > 20*30 * 1000) {
System.out.println(now);
System.out.println(createHashDate.get(key));
synchronized (hash) {
hash.remove(key);
}
synchronized (count) {
count--;
}
}
}
//每10秒钟进行一次用户登录信息检测
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
稍微解释下此段代码
当web服务器启动的时候,比如tomcat启动的时候,该类将被实列化,生命周期直到tomcat关闭的时候结束
服务器services.xml
配置如下
<service xmlns="http://xfire.codehaus.org/config/1.0">
<name>demoService</name>
<namespace>demoService</namespace>
<serviceClass>com.kiloway.webservice.demo.Iservice</serviceClass>
<implementationClass>com.kiloway.webservice.demo.serviceImpl</implementationClass>
<inHandlers>
<handler handlerClass="com.kiloway.webservice.demo.AuthenticationHandler"></handler>
</inHandlers>
<mce:style><!--
wrapped
--></mce:style><style mce_bogus="1">wrapped</style>
<use>literal</use>
<scope>application</scope>
</service>
</beans>
.net客户端调用的时候添加一个类
namespace ConsoleApplication1
{
[System.Serializable]
[System.Xml.Serialization.XmlType(Namespace = "demoService")]
[System.Xml.Serialization.XmlRoot(Namespace = "demoService", IsNullable = false)]
public class AuthenticationToken : SoapHeader
{
public string name = null;
public string password = null;
}
}
然后再生成的代理类里面添加
在生成的代理类的调用方法上再添加如下代码
在调用该方法时候,验证信息将被加入soap header中
调用采用
纠正一点,其实Java与C#通过Webservice通信的安全控制还是可以用session来控制的,只是客户端需要保存一cookie,C#提供了一个cookieContainer来为我们保存Session