上一篇文章地址:構(gòu)建安全的xml web service系列一之初探使用soap頭
要分析xml web service的安全性,首先要解決的問(wèn)題是我們能了解和清楚soap消息的格式和內(nèi)容,如果獲得不了soapmessage,分析如何能構(gòu)建安全xml web service也就無(wú)從下手,即使分析出來(lái),自己也可能模模糊糊,不能定論。下面就分析下如何獲得soapmessage。
首先介紹一個(gè)類(lèi)-soapextension,msdn對(duì)這個(gè)類(lèi)的備注為:asp.net 允許通過(guò)擴(kuò)展性機(jī)制生成與 soap 相關(guān)的基礎(chǔ)結(jié)構(gòu)。asp.net soap 擴(kuò)展結(jié)構(gòu)以一種擴(kuò)展為中心,該擴(kuò)展可以在客戶(hù)端或服務(wù)器上處理消息時(shí)在特定階段中檢查或修改消息。asp.net soap 擴(kuò)展從 soapextension 類(lèi)派生。getinitializer 和 initialize 方法提供其他可用機(jī)制,用于初始化 soap 擴(kuò)展以增強(qiáng)性能。processmessage 是大多數(shù) soap 擴(kuò)展的核心,原因是該方法在 soapmessagestage 中定義的每一個(gè)階段都被調(diào)用,從而使 soap 擴(kuò)展得以執(zhí)行所需的該特定 soap 擴(kuò)展的行為。對(duì)于需要修改 soap 請(qǐng)求或 soap 響應(yīng)的 soap 擴(kuò)展,chainstream 提供一個(gè)機(jī)會(huì)以接收要通過(guò)網(wǎng)絡(luò)發(fā)送的建議數(shù)據(jù)。 仔細(xì)閱讀這段文字,如果您以前開(kāi)發(fā)過(guò)windows程序,那第一個(gè)應(yīng)該想到的是:原來(lái)web service的處理機(jī)制和windows窗口程序的消息機(jī)制竟然有著一曲同工之妙。下面談?wù)勅绾卫眠@個(gè)類(lèi),來(lái)截獲xml web service請(qǐng)求和相應(yīng)的soap消息,從而看看xml web service的廬山真面目。
首先大家先看看這個(gè)類(lèi),這個(gè)類(lèi)完成的功能是將xml web service通過(guò)擴(kuò)展的方式,將每次的請(qǐng)求和響應(yīng)的soap消息通過(guò)日志的方式保存到文本文件中。日志記錄的方式也有兩種:
1。針對(duì)每個(gè)webmethod產(chǎn)生一個(gè)日志文件。
2。針對(duì)每個(gè)webservice產(chǎn)生一個(gè)日志文件
因?yàn)橐粋€(gè)webservice可能包含一個(gè)或者多個(gè)webmethod,所以如果指定兩種方法都支持的話,那第二個(gè)日志兩面應(yīng)該包括第一個(gè)日志里面的內(nèi)容,而有些情況下,是不需要對(duì)每個(gè)webmethod都進(jìn)行日志記錄的,這時(shí)候采用第一種記錄方式,增強(qiáng)了系統(tǒng)的靈活性。
下面是擴(kuò)展了的soapextension
<webservices>
<soapextensiontypes>
<add type="jillzhang.traceextension,jillzhang" priority="1" group="high" />
</soapextensiontypes>
</webservices>
可以記錄soapmessage的soapextension
namespace jillzhang
{
public class traceextension: soapextension
{
static readonly string logroot = system.configuration.configurationmanager.appsettings["logroot"];
stream oldstream;
stream newstream;
string filename;
/// <summary>
/// 將請(qǐng)求流和響應(yīng)流存到內(nèi)存流中,已被調(diào)用
/// </summary>
/// <param name="stream">包含 soap 請(qǐng)求或響應(yīng)的內(nèi)存緩沖區(qū)</param>
/// <returns>它表示此 soap 擴(kuò)展可以修改的新內(nèi)存緩沖區(qū)。</returns>
public override stream chainstream(stream stream)
{
oldstream = stream;
newstream = new memorystream();
return newstream;
}
/// <summary>
/// 在xml web service第一次運(yùn)行的時(shí)候,一次性的將通過(guò)traceextensionattribute傳遞進(jìn)來(lái)的
/// 保存日志信息的文件名初始化
/// </summary>
/// <param name="methodinfo">應(yīng)用 soap 擴(kuò)展的 xml web services 方法的特定函數(shù)原型</param>
/// <param name="attribute">應(yīng)用于 xml web services 方法的 soapextensionattribute</param>
/// <returns>soap 擴(kuò)展將對(duì)其進(jìn)行初始化以用于緩存</returns>
public override object getinitializer(logicalmethodinfo methodinfo, soapextensionattribute attribute)
{
return ((traceextensionattribute)attribute).filename;
}
/// <summary>
/// 替代為每個(gè)方法配置的保存soapmessage文件名,而是將整個(gè)網(wǎng)絡(luò)服務(wù)
/// 的soapmessage都保存到一個(gè)日志文件中,這個(gè)文件路徑需要在web service
/// 的配置文件中web.config指出,如
/// <appsettings>
/// <add key="logroot" value="c://servicelog"/>
/// </appsettings>
/// </summary>
/// <param name="webservicetype">網(wǎng)絡(luò)服務(wù)的類(lèi)型</param>
/// <returns>用于保存日志記錄的文件路徑</returns>
public override object getinitializer(type webservicetype)
{
//return logroot.trimend('//') + "http://" + webservicetype.fullname + ".log";
return logroot.trimend('//')+"http://"+ webservicetype.fullname + ".log";
}
//獲得文件名,并將其保存下來(lái)
public override void initialize(object initializer)
{
filename = (string)initializer;
}
/// <summary>
/// 當(dāng)數(shù)據(jù)還為soap格式的時(shí)候,將數(shù)據(jù)寫(xiě)入日志
/// </summary>
/// <param name="message"></param>
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");
}
}
/// <summary>
/// 將soapmessage寫(xiě)入到日志文件
/// </summary>
/// <param name="message"></param>
public void writeoutput(soapmessage message)
{
newstream.position = 0;
//創(chuàng)建或追加記錄文件
filestream fs = new filestream(filename, filemode.append,
fileaccess.write);
streamwriter w = new streamwriter(fs);
string soapstring = (message is soapservermessage) ? "soap響應(yīng)" : "soap請(qǐng)求";
w.writeline("-----" + soapstring + " 在 " + datetime.now.tostring("yyyy年mm月dd日 hh時(shí)mm分ss秒"));
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) ?
"soap請(qǐng)求" : "soap響應(yīng)";
w.writeline("-----" + soapstring +
" 在 " + datetime.now.tostring("yyyy年mm月dd日 hh時(shí)mm分ss秒"));
w.flush();
newstream.position = 0;
copy(newstream, fs);
w.close();
newstream.position = 0;
}
/// <summary>
/// 拷貝流到流
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
void copy(stream from, stream to)
{
textreader reader = new streamreader(from);
textwriter writer = new streamwriter(to);
writer.writeline(reader.readtoend());
writer.flush();
}
}
//創(chuàng)建一個(gè)用于在webmethod上使用的soapextension屬性
[attributeusage(attributetargets.method)]
public class traceextensionattribute : soapextensionattribute
{
private string filename = "c://log.txt";
private int priority;
/// <summary>
/// 擴(kuò)展類(lèi)型
/// </summary>
public override type extensiontype
{
get { return typeof(traceextension); }
}
/// <summary>
/// 優(yōu)先級(jí)
/// </summary>
public override int priority
{
get { return priority; }
set { priority = value; }
}
/// <summary>
/// 用于記錄該webmethod的soapmessage的文件的絕對(duì)路徑
/// 默認(rèn)為c://log.txt;
/// </summary>
public string filename
{
get
{
return filename;
}
set
{
filename = value;
}
}
}
}
結(jié)下來(lái),介紹一個(gè)如何使用該類(lèi):
如果要使讓traceextension支持第一種記錄方式,需要作的額外工作為:
只需要在要記錄soapmessage的webmethod添加如下的attribute
[traceextension(filename="d://data.xml",priority=0)]
當(dāng)然路徑,您可以自己設(shè)定
前一節(jié)的webmethod就變成了
添加了針對(duì)webmethod日志記錄的webmethod
public mysoapheader header = new mysoapheader();
[webmethod]
[soapheader("header")]
[traceextension(filename="d://data.xml",priority=0)]
public string helloworld()
{
if (header == null)
{
return "您沒(méi)有設(shè)置soapheader,不能正常訪問(wèn)此服務(wù)!";
}
if (header.username != "jillzhang" || header.pwd != "123456")
{
return "您提供的身份驗(yàn)證信息有誤,不能正常訪問(wèn)此服務(wù)!";
}
return "hello world";
}
調(diào)用下該webservice,便在d盤(pán)產(chǎn)生一個(gè)data.xml文件,里面的內(nèi)容為:
-----soap請(qǐng)求 在 2007年05月25日 09時(shí)06分29秒
<?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="
-----soap響應(yīng) 在 2007年05月25日 09時(shí)06分29秒
<?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="
如何采用第二種方法,讓webservice的每個(gè)webmethod都能記錄日志
需要在web.config中作如下的設(shè)定
首先添加如下節(jié)點(diǎn)
<webservices>
<soapextensiontypes>
<add type="jillzhang.traceextension,jillzhang" priority="1" group="high" />
</soapextensiontypes>
</webservices>
然后通過(guò)配置設(shè)定日志文件保留的路徑:
<appsettings>
<add key="logroot" value="d:"/>
</appsettings>
找到日志文件,里面也赫然有著soapmessage的真面目。通過(guò)以上方法,大家可以清晰地分析到soapmessage的具體格式和內(nèi)容,知道了這個(gè)以后,對(duì)付web service,您就可以隨心應(yīng)手,隨心所欲,如果你愿意,你甚至也可以“強(qiáng)奸"一次webservice,哈哈!
新聞熱點(diǎn)
疑難解答
圖片精選