加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Webservice架构设计

发布时间:2020-12-16 23:28:19 所属栏目:安全 来源:网络整理
导读:关于j2ee中webservice的搭建以及不同系统中的访问,我已经在一篇博文《webservice之cxf实现》中进行了介绍,下面我们来谈谈webservice的架构设计要考虑的一些因素。 ??最首要的因素就是安全性,比如:如果验证调用者的合法身份?如果保证数据传输的安全性?

关于j2ee中webservice的搭建以及不同系统中的访问,我已经在一篇博文《webservice之cxf实现》中进行了介绍,下面我们来谈谈webservice的架构设计要考虑的一些因素。

??最首要的因素就是安全性,比如:如果验证调用者的合法身份?如果保证数据传输的安全性?等等。

??先来看调用者身份的合法性验证问题。一般情况下ws底层使用http做为传输协议,http本身是无状态的,所以,我们要确保调用者的唯一身份,就要求调用者在调用时,携带身份标识参数。身份标识可以采用用户名加密码的方式实现,webservice标准中ws-security部分已经有了相应的标准。

??第二个问题是数据传统过程中的安全性问题,服务器端和客户端如何知道信息的来源可靠、真实。其实这个问题在互联网应用中广泛存在的问题,并不是webservice考虑的范畴,但是,ws底层采用http协议传输,固然存在安全情况的问题(如果仅仅在公司内部局域网中的服务器群中调用,安全性要求就没有这么高了,呵呵)。

??http验证信息的准确性,可采用的方式很多,比如:md5签名,时间戳、消息自身加密、https等等,下面我们先来看使用cxf如何对用户验证,并确保信息的安全性。

??在服务器端,通过AuthCheckOnServer类对用户名密码进行验证,在配置com.my.webservice.WebServiceFacadeImpl的时候通过拦截器加入AuthCheckOnServer,注意,在action中加了Timestamp,而passwordType采用PasswordDigest,实现密码部分的加密(你可以通过抓包工具观察密码加密效果),服务器上拦截类代码如下:

AuthCheckOnServer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AuthCheckOnServer implements CallbackHandler {  @Override  public void handle(Callback[] callbackArray) throws IOException,  UnsupportedCallbackException {  if (callbackArray.length > 0) {  WSPasswordCallback pc = (WSPasswordCallback)callbackArray[0];  String userId = pc.getIdentifier();  System.out.println("server得到用户名:" + userId );  password = "此处根据用户标识userId,通过service(或dao)查询该用户的密码,我略去了...";  //设置好用查出的密码,此处是明文,cxf自动生成密文并进行校验  setPassword(password);  }  } }

服务器ws服务类配置如下:

14 15
<jaxws:endpoint id="myWebService" address="/myWebService"  implementor="com.my.webservice.WebServiceFacadeImpl">  <jaxws:inInterceptors>  <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />  "org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"<constructor-arg>  <map>  <entry key="action" value="Timestamp UsernameToken" "passwordType" "PasswordDigest" "passwordCallbackClass" "com.my.webservice.AuthCheckOnServer" </map>  </constructor-arg>  </bean>  </jaxws:inInterceptors>  </jaxws:endpoint> 

??在cxf的客户端调用代码中,也加上拦截器,在调用请求中加入用户名及密码相关信息,客户端拦截类代码如下:

AuthPrepareProcesser4Client.java
12
AuthPrepareProcesser4Client CallbackHandler { @Override setPassword("123");  setIdentifier("yanwawa");  "Client setting userName and password OK.");  } } } 

??客户端spring的客户端配置代码如下:

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
"wsClient" "com.my.webservice.IWebServiceFacade"  factory-bean="wsClientProxy" factory-method="create" "org.apache.cxf.jaxws.JaxWsProxyFactoryBean"<property name="serviceClass" "com.my.webservice.IWebServiceFacade" "address"  "http://localhost:8088/web_service_server/services/myWebService" "inInterceptors"<list>  <ref bean="logIn" </list>  </property>  "outInterceptors""logOut" "saajOut" "wss4jOut" "wss4jOut" "org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor""user" "yanwawa" "com.my.webservice.AuthPrepareProcesser4Client" "logIn" "org.apache.cxf.interceptor.LoggingInInterceptor" "logOut" "org.apache.cxf.interceptor.LoggingOutInterceptor" "saajOut" "org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" /> 

??当然,要以axis的soap客户端调用,代码要复杂一些,会用到WSSecEncrypt等几个类,也就是用这些类来帮助我们生成要发送的消息体,具体实现请参考axis的sample。

??在传输过程中,为了确保数据的安全,我们可以在服务器上配置https支持,保证传输中的数据相对安全。

??下面我给出另外一种不依赖ws-security的调用方式,当然,我是借鉴网上支付的数字签名的设计方式(网上支付实现请参阅《项目集成第三方支付设计方案》),即完全由程序自己来实现用户身份的验证,这种方案相对来说比较通用。

??类图设计如下所示:

webservice sign架构图

??在这种设计方案中,ws调用被封装到外观中,由于这种方式需要客户端上传用户id以及验证签名,所以外观类中的所有方法签名都带有这两个参数,似乎有了入侵,但话又说回来,这种方式并不影响service对象的方法设计,而且这种设计更有利于其它非java系统的ws调用。

??非核心代码我都略去了,仅贴出客户端调用及服务器端验证的sayHello、add方法代码片断,客户端代码:

15
BeanFactory bf = new ClassPathXmlApplicationContext("classpath:ws-client.xml");  IWebServiceFacade wsClient = (IWebServiceFacade)bf.getBean("wsClient");  clientId = "1";  privateKey = "client1_md5_privateKey";  Map reqMap = HashMap();  reqMap.put("name", sign = SignatureHelper.sign(reqMap, privateKey);  sayHelloResult = wsClient.sayHello("yanwawa",210)!important">clientId,210)!important">sign);  println(sayHelloResult);  "number1",152)!important">"9");  "number2", 2);  privateKey);  int addResult = add("9",152)!important">2,210)!important">addResult); 

??服务器端代码:

WebServiceFacadeImpl.java
31
@WebService(endpointInterface="com.my.webservice.IWebServiceFacade") WebServiceFacadeImpl IWebServiceFacade {  //业务接口,由spring注入  private IBizService bizService;  @Override  public String sayHello(name,210)!important">signStr) {  //auth check  privateKey = bizService.getClientPrivateKey(clientId);  name);  verifySignCode(privateKey,210)!important">signStr)) {  return name);  } else {  //log and throws Exception  throw new IllegalArgumentException("calling ws method 'sayHello',sign code error!");  }  }  int add(number1, number2,210)!important">number1);  number2);  number2);  } "calling ws method 'add',sign code error!");  }  } //……………………….其它代码略 

??总的来说,用第一种用户名加密码的方式确保用户的每一次调用的合法性在cxf中是不错的选择,因为它是在拦截器加入用户名及密码信息,服务器端的验证也是在拦截器中做的,因此,这种方式的代码看起来更简洁,可读性更好。

??第二种方式类似于的线支付的签名验证方式,比较通用,但是方法签名都要携带验证信息,看起来比较累赘,但由于http的无状态性,身份标识每次都提交到服务器是一种比较简单的方法。第二种方式还有一个问题需要注意,就是客户端也要通过签名验证返回消息的合法性,

??当然,任何东西都有两面性,好比一把“双刃剑”,我们只有根据项目的实际需求,平衡各种因素,扬长避短,选择最适合、最实用的一种方式,解决问题才是最终的目的。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

相关内容
推荐文章
站长推荐
热点阅读