CSDN博客

img yiruoyun

Soap协议扩展的应用及实现

发表于2004/10/18 17:33:00  1660人阅读

Soap协议扩展的应用及实现




Soap协议 (简单对象传输)是一种基于 XML 的、用于在 Web 上交换结构化和类型信息的简单的轻量协议。SOAP 的总体设计目标是使其尽可能地简单,并提供最少的功能。该协议定义一个不包含任何应用程序或传输语义的消息处理框架。因此,该协议是模块化的,并具有很强的扩展性。
Soap 协议规范包含四个主要组成部分。第一部分定义用于封装数据的必需的可扩展信封。该 SOAP 信封定义 SOAP 消息,并且是 SOAP 消息处理器之间的基本交换单位。这是该规范唯一必需的部分。
SOAP 协议规范的第二部分定义用来表示应用程序定义的数据类型和有向图形的可选数据编码规则,以及用于序列化非句法数据模型的统一模型。
第三部分定义 RPC 样式(请求/响应)的消息交换模式。每个 SOAP 消息都是单向传输。尽管 SOAP 的根位于 RPC 中,但它不仅仅只是请求/响应机制。XML Web services 经常组合 SOAP 消息以实现此类模式,但 SOAP 并不强制要求消息交换模式,这部分规范也是可选的。
规范的第四部分定义 SOAP 和 HTTP 之间的绑定。但该部分也是可选的。可以将 SOAP 与任何能够传输 SOAP 信封的传输协议或机制(包括 SMTP FTP 甚至软盘)结合在一起使用。
Microsoft公司实现了soap协议,并使之成为.Net Framework的一个重要的基础.Microsoft.net的任何基于client-server的传输都是以soap的形式的.(httpGet 及httpPost 协议只是client应用).
举一个.Net的例子.如一个web service程序是 data.asmx:
<%@ WebService Language="C#" Class="data" %>
using System;
using System.Web.Services;
public class Order
{
public int OrderID;
public double Price;
}
public class data {
[WebMethod]
public Order GetOrder()
{
Order myOrder = new Order();
myOrder.Price=34.5;
myOrder.OrderID = 323232;
return myOrder;
}
}
现再用wsdl工具将web serivce的wsdl转换成client的代理程序,如:wsdl /protocol:Soap /namespace:D http://localhost/data1.asmx
(假设data1.asmx是放在iis服务器的根目录下(c:/inetpub/wwwroot)).
产生一个客户端的代理程序data.cs:
namespace D {
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;
/// <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="dataSoap", Namespace="http://tempuri.org/")]
public class data : System.Web.Services.Protocols.SoapHttpClientProtocol {
/// <remarks/>
public data() {
this.Url = "http://localhost/data1.asmx";
}
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/GetOrder", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public Order GetOrder() {
object[] results = this.Invoke("GetOrder", new object[0]);
return ((Order)(results[0]));
}
/// <remarks/>
public System.IAsyncResult BeginGetOrder(System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("GetOrder", new object[0], callback, asyncState);
}
/// <remarks/>
public Order EndGetOrder(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((Order)(results[0]));
}
}
/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]
public class Order {
/// <remarks/>
public int OrderID;
/// <remarks/>
public System.Double Price;
}
}
然后再将之编译成dll.如:csc /t:library /out:c:/inetpub/wwwroot/bin/data.dll data.cs.完成以后就可以使用.Net的client程序进行调用(如winform ,cs or aspx).
下面是用test.aspx实现client的调用的.
<%@ Import Namespace="D" %>
<html>
<style>
div
{
font: 8pt verdana;
background-color:cccccc;
border-color:black;
border-width:1;
border-style:solid;
padding:10,10,10,10;
}
</style>
<script language="C#" runat="server">
public void Page_Load(Object sender, EventArgs E)
{
data datatype =new data();
Order or=datatype.GetOrder();
Response.Write("order -==="+or.OrderID+"<br>");
}
</script>
</body>
</html>
运行这个程序,将在浏览器打印"order=======323232".
我们来研究client 同server到底传输了什么.
当我们执行test.aspx时,它调用到了data.cs的GetOrder()方法,然后序列化信息,并包装成:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetOrder xmlns="http://tempuri.org/" /></soap:Body></soap:Envelope>
传输到server上,server接受信息以后,反序列化,然后调用server上的GetOrder()方法,得到一个返回值并序列化,得到一个流:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetOrderResponse xmlns ="http://tempuri.org/"><GetOrderResult><OrderID>323232</OrderID><Price>34.5
</Price></GetOrderResult></GetOrderResponse></soap:Body></soap:Envelope>传输到client端.client端收到信息后再反序列得到值.
从上分析可知,soap协议就是一个传输数据的作用.但网络的传输是复杂的且有许多的不定因素,怎样去考虑安全性呢?client怎样确定server发的是可靠的信息而server又怎样辨认client呢?
Microsoft推出了Soap extension(扩展),来实现soap消息的加密与解密.
从下图可以看出soap扩展的用途:

正如您看到的,ASP.NET 在 XML Web services 计算机和 XML Web services 客户端计算机上的阶段中对 XML 进行序列化和反序列化。可以将 SOAP 扩展插入基础结构以在每个序列化和反序列化阶段的之前和之后检查或修改 SOAP 消息。例如,加密 SOAP 扩展可能在 ASP.NET 序列化客户端的参数之后加密 SOAP 消息的 XML 部分,而后在 ASP.NET 反序列化 SOAP 消息之前在 Web 服务器上解密 SOAP 消息。这些阶段(SOAP 扩展可能在这些阶段中检查或修改 SOAP 消息)是在 SoapMessageStage 枚举中定义的。在这种情况下,在 AfterSerialize 阶段加密 SOAP 扩展,在 BeforeDeserialize 阶段解密 SOAP 扩展。
通常,当 SOAP 扩展修改 SOAP 消息的内容时,必须在客户端和服务器上都进行修改。也就是说,如果要在客户端上运行 SOAP 扩展并加密 SOAP 消息,则相应的 SOAP 扩展必须在服务器上对该 SOAP 消息进行解密。如果未解密该 SOAP 消息,则 ASP.NET 基础结构不能将 SOAP 消息反序列化为对象。当然,不修改 SOAP 消息的 SOAP 扩展(例如仅记录 SOAP 消息的 SOAP 扩展)可以只在客户端或服务器上运行。在这种情况下,接收方接收与在 SOAP 扩展不运行并且 ASP.NET 基础结构可以反序列化 SOAP 消息的情况下相同的 SOAP 消息。另外,如果 SOAP 扩展不以一种使反序列化不能实现的方式对 SOAP 进行修改,则 SOAP 扩展不需要既运行在客户端又运行在服务器上。
调用 SOAP 扩展方法的顺序
我们看看在整个 XML Web services 方法调用过程中 ASP.NET 何时调用 SOAP 扩展方法。下面的步骤假设 SOAP 扩展既运行于客户端又运行于服务器上。如果 SOAP 扩展不同时运行于客户端和服务器上,则 ASP.NET 将忽略与 SOAP 扩展运行于其中任何一个相关联的步骤。
客户端
1. 客户端对代理类调用方法。
2. 在客户端上创建 SOAP 扩展的新实例。
3. 如果这是第一次在客户端上用该 XML Web services 执行该 SOAP 扩展,则对运行于客户端上的 SOAP 扩展调用 GetInitializer 方法。
4. 调用 Initialize 方法。
5. 调用 ChainStream 方法。
6. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 BeforeSerialize。
7. 客户端计算机上的 ASP.NET 将 XML Web services 方法的参数序列化为 XML。
8. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 AfterSerialize。
9. 客户端计算机上的 ASP.NET 通过网络将 SOAP 消息发送到承载该 XML Web services 的 Web 服务器。
服务器端
1. Web 服务器上的 ASP.NET 接收该 SOAP 消息。
2. 在 Web 服务器上创建 SOAP 扩展的新实例。
3. 在 Web 服务器上,如果这是第一次在服务器端使用该 XML Web services 执行该 SOAP 扩展,则对运行于服务器上的 SOAP 扩展调用 GetInitializer 方法。
4. 调用 Initialize 方法。
5. 调用 ChainStream 方法。
6. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 BeforeDeserialize。
7. ASP.NET 反序列化 XML 中的参数。
8. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 AfterDeserialize。
9. ASP.NET 创建实现 XML Web services 的类的新实例并调用 XML Web services 方法,同时传入反序列化的参数。该对象驻留在与 Web 服务器相同的计算机上。
10. XML Web services 方法执行其代码,最终会设置返回值和任何输出参数。
11. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 BeforeSerialize。
12. Web 服务器上的 ASP.NET 将返回值和输出参数序列化为 XML。
13. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 AfterSerialize。
14. ASP.NET 通过网络将 SOAP 响应消息发送回 XML Web services 客户端。
客户端
1. 客户端计算机上的 ASP.NET 接收 SOAP 消息。
2. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 BeforeDeserialize。
3. ASP.NET 将 XML 反序列化为返回值和任何输出参数。
4. 调用 ProcessMessage 方法,调用时将 SoapMessageStage 设置为 AfterDeserialize。
5. ASP.NET 将返回值和任何输出参数传递到代理类的实例。
6. 客户端接收返回值和任何输出参数。
我们现在以.Net的一个例子说明soap扩展的应用.
首先,我们写一个soap extension的程序,它是继承SoapExtension而来.
( TraceExtension.cs)
using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
using System.Net;
// Define a SOAP Extension that traces the SOAP request and SOAP
// response for the XML Web service method the SOAP extension is
// applied to.
public class TraceExtension : SoapExtension
{
Stream oldStream;
Stream newStream;
string filename;
// Save the Stream representing the SOAP request or SOAP response into
// a local memory buffer.
public override Stream ChainStream( Stream stream ){
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
// When the SOAP extension is accessed for the first time, the XML Web
// service method it is applied to is accessed to store the file
// name passed in, using the corresponding SoapExtensionAttribute.
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
Stream fs = new FileStream("d://temp//a.txt", FileMode.Create);
StreamWriter writer=new StreamWriter(fs);
writer.WriteLine("extension is ok ");
//writer.Flush();
//fs.Close();
writer.Close();
return ((TraceExtensionAttribute) attribute).Filename;
}
// The SOAP extension was configured to run using a configuration file
// instead of an attribute applied to a specific XML Web service
// method.
public override object GetInitializer(Type WebServiceType)
{
// Return a file name to log the trace information to, based on the
// type.
return WebServiceType.GetType().ToString() + ".log";
}
// Receive the file name stored by GetInitializer and store it in a
// member variable for this specific instance.
public override void Initialize(object initializer)
{
Stream fs = new FileStream("d://temp//b.txt", FileMode.Create);
StreamWriter writer=new StreamWriter(fs);
writer.WriteLine("extension bb is ok ");
//writer.Flush();
//fs.Close();
filename = (string) initializer; //监控文件是:c:/winnt/system32/system.runtimetype.log
writer.WriteLine("filename ========="+filename);
writer.Close();
}
// If the SoapMessageStage is such that the SoapRequest or
// SoapResponse is still in the SOAP format to be sent or received,
// save it out to a file.
public override void ProcessMessage(SoapMessage message) //处理传输的数据.
{
switch (message.Stage) {
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
WriteOutput(message);
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput(message);
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}
public void WriteOutput(SoapMessage message){// 可以添加解密算法
newStream.Position = 0;
FileStream fs = new FileStream(filename, FileMode.Append,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
string soapString = (message is SoapServerMessage) ? "SoapResponse" : "SoapRequest";
w.WriteLine("-----out ----" + soapString + " at " + DateTime.Now);
w.WriteLine("message ====="+message.MethodInfo.Name);
w.Flush();
Copy(newStream, fs);
w.Close();
newStream.Position = 0;
Copy(newStream, oldStream);
}
public void WriteInput(SoapMessage message){//可以添加加密算法
Copy(oldStream, newStream);
FileStream fs = new FileStream(filename, FileMode.Append,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
string soapString = (message is SoapServerMessage) ?
"SoapRequest" : "SoapResponse";
w.WriteLine("-----int -------" + soapString +
" at " + DateTime.Now);
w.Flush();
newStream.Position = 0;
Copy(newStream, fs);
w.Close();
newStream.Position = 0;
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
// Create a SoapExtensionAttribute for the SOAP Extension that can be
// applied to an XML Web service method.
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute {
private string filename = "c://log.txt";
private int priority;
public override Type ExtensionType {
get { return typeof(TraceExtension); }
}
public override int Priority {
get { return priority; }
set { priority = value; }
}
public string Filename {
get {
return filename;
}
set {
filename = value;
}
}
}
我们将它编译成dll文件,如:csc /t:library /out:c:/inetpub/wwwroot/bin/TraceExtension.dll TraceExtension.cs
然后我们再写一个配置文件: web.config:
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="TraceExtension,TraceExtension" priority="1" group="0" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
将web.config存入c:/inetpub/wwwroot目录.
最后我们再运行test.aspx,我们可以在c:/winnt/system32/system.runtimetype.log文件中找到client与server端所传输的数据.我们可以在TraceExtension.cs 中的ProcessMessage(SoapMessage)方法中加上加密及解密算法,改变client-server传输的数据.
总之,soap扩展在安全保密方面是非常有用的.
阅读全文
0 0

相关文章推荐

img
取 消
img