轉(zhuǎn)帖|其它|編輯:郝浩|2010-11-29 15:53:34.000|閱讀 2177 次
概述:本文介紹一下在WCF中使用SoapHeader進(jìn)行驗(yàn)證的兩種實(shí)現(xiàn)方法,同時(shí)再次復(fù)習(xí)自定義Inspector和自定義EndpointBehavior。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
本文介紹一下在WCF中使用SoapHeader進(jìn)行驗(yàn)證的兩種實(shí)現(xiàn)方法,同時(shí)再次復(fù)習(xí)自定義Inspector和自定義EndpointBehavior。
在Xml Web Service中能將用戶的身份信息如用戶名,密碼添加到SoapHeader中,從而實(shí)現(xiàn)服務(wù)調(diào)用的身份驗(yàn)證,這種做法是沿用了Http中用戶名,密碼身份驗(yàn)證,是我們最樂(lè)于接受的。而在WCF中因?yàn)樘峁┝朔浅=训陌踩珯C(jī)制,但實(shí)現(xiàn)起來(lái)真是不夠簡(jiǎn)單。對(duì)于多數(shù)應(yīng)用情景來(lái)講,有點(diǎn)大炮打蚊子的感覺(jué)。因此好多人在網(wǎng)上詢問(wèn)在WCF中如何象XMl Web Service一樣使用SoapHeader來(lái)完成用戶名,密碼身份驗(yàn)證。傳統(tǒng)的辦法是通過(guò)在服務(wù)的操作中從OperationContext.Current.IncomingMessageHeaders來(lái)獲取Header中的內(nèi)容,而在客戶端在OperationContext.Current.OutgoingMessageHeaders中添加MessageHeader。下面的代碼片段簡(jiǎn)要的介紹了這種實(shí)現(xiàn):
在服務(wù)端的一個(gè)Operation中
public string GetData(int value)
{
System.Text.Encoding encoding = System.Text.Encoding.GetEncoding("utf-8");
string username = "";
string pwd = "";
int index =
OperationContext.Current.IncomingMessageHeaders.
FindHeader("username", "//tempuri.org");
if (index >= 0)
{
username =
OperationContext.Current.IncomingMessageHeaders.
GetHeader<string>(index).ToString();
}
index = OperationContext.Current.IncomingMessageHeaders.
FindHeader("pwd", "//tempuri.org");
if (index >= 0)
{
pwd = OperationContext.Current.IncomingMessageHeaders.
GetHeader<string>(index).ToString();
}
return string.Format("You entered: {0}", value);
}
在客戶端調(diào)代碼如下:
Robin_Wcf_Formatter_Svc.Service1Client svc =
new Robin_Wcf_Formatter_Svc.Service1Client();
using (OperationContextScope scope =
new OperationContextScope(svc.InnerChannel))
{
MessageHeader header =
MessageHeader.CreateHeader("username", "//tempuri.org", "robinzhang");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
header = MessageHeader.CreateHeader("pwd", "//tempuri.org", "robinzhang");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
string res = svc.GetData(10);
}
通過(guò)上邊的代碼實(shí)現(xiàn),已經(jīng)能在WCF中使用SoapHeader來(lái)傳遞身份信息了。但這種方式需要在每次客戶端調(diào)用和每個(gè)服務(wù)操作中都增加類似代碼片斷。比較麻煩。多數(shù)情況下,我們的服務(wù)開(kāi)發(fā)好之后,往往只開(kāi)放給固定的用戶用于消費(fèi),如果我們的服務(wù)的實(shí)例模式為PerCall,也就是不保存會(huì)話,同時(shí)我們又希望能驗(yàn)證調(diào)用者的身份信息,我們需要在每個(gè)Operation的消息中增加SoapHeader來(lái)附加身份信息。這樣服務(wù)即可保證每一個(gè)操作都不被非法調(diào)用。閱讀完上篇文章,已經(jīng)了解到通過(guò)MessageInspector能攔截消息用于記錄或者修改,如果在攔截到消息之后,在消息中增加MessageHeader便可以實(shí)現(xiàn)上述需求。為此我們實(shí)現(xiàn)了一個(gè)實(shí)現(xiàn)IClientMessageInspector, IDispatchMessageInspector, IEndpointBehavior三個(gè)接口的類,這樣該類就承擔(dān)了兩種角色,自定義MessageInspector,自定義EndpointBehavior。這個(gè)類的代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel;
namespace RobinLib
{
public class AttachUserNamePasswordBehavior : IClientMessageInspector, IDispatchMessageInspector, IEndpointBehavior
{
private static string UserName =
System.Configuration.ConfigurationSettings.AppSettings["username"];
private static string Password =
System.Configuration.ConfigurationSettings.AppSettings["pwd"];
public AttachUserNamePasswordBehavior()
{
}
#region IClientMessageInspector 成員
public void AfterReceiveReply(ref System.
ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest
(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel)
{
MessageHeader userNameHeader =
MessageHeader.CreateHeader
("OperationUserName", "//tempuri.org", UserName, false, "");
MessageHeader pwdNameHeader =
MessageHeader.CreateHeader
("OperationPwd", "//tempuri.org", Password, false, "");
request.Headers.Add(userNameHeader);
request.Headers.Add(pwdNameHeader);
Console.WriteLine(request);
return null;
}
#endregion
#region IDispatchMessageInspector 成員
string GetHeaderValue(string key)
{
int index = OperationContext.Current.IncomingMessageHeaders.FindHeader
(key, "//tempuri.org");
if (index >= 0)
{
return OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index).
ToString();
}
return null;
}
public object AfterReceiveRequest
(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext)
{
Console.WriteLine(request);
string username = GetHeaderValue("OperationUserName");
string pwd = GetHeaderValue("OperationPwd");
if (username == "robinzhang" && pwd == "111111")
{
}
else
{
throw new Exception("操作中的用戶名,密碼不正確!");
}
return null;
}
public void BeforeSendReply(ref System.ServiceModel.
Channels.Message reply, object correlationState)
{
}
#endregion
#region IEndpointBehavior 成員
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add
(new AttachUserNamePasswordBehavior());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new AttachUserNamePasswordBehavior());
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
}
象上文一樣,將自定義的EndpointBehavior通過(guò)代碼方式應(yīng)用到Host和Proxy中
服務(wù)宿主程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Robin_Wcf_OperationWithToken_Host
{
public class Program
{
static void Main(string[] args)
{
//服務(wù)地址
Uri baseAddress = new Uri("net.tcp://127.0.0.1:8081/Robin_Wcf_Formatter");
ServiceHost host =
new ServiceHost(typeof(Robin_Wcf_OperationWithToken_SvcLib.Service1),
new Uri[] { baseAddress });
//服務(wù)綁定
NetTcpBinding bind = new NetTcpBinding();
host.AddServiceEndpoint(typeof
(Robin_Wcf_OperationWithToken_SvcLib.IService1), bind, "");
if (host.Description.Behaviors.Find
<System.ServiceModel.Description.ServiceMetadataBehavior>() == null)
{
System.ServiceModel.Description.ServiceMetadataBehavior svcMetaBehavior =
new System.ServiceModel.Description.ServiceMetadataBehavior();
svcMetaBehavior.HttpGetEnabled = true;
svcMetaBehavior.HttpGetUrl = new Uri("//127.0.0.1:8001/Mex");
host.Description.Behaviors.Add(svcMetaBehavior);
}
host.Opened += new EventHandler(delegate(object obj, EventArgs e)
{
Console.WriteLine("服務(wù)已經(jīng)啟動(dòng)!");
});
foreach (var sep in host.Description.Endpoints)
{
sep.Behaviors.Add(new RobinLib.AttachUserNamePasswordBehavior());
}
host.Open();
Console.Read();
}
}
}
客戶端代理
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class Service1Client : System.ServiceModel.ClientBase<
Robin_Wcf_OperationWithToken_ClientApp.ServiceReference1.IService1>, Robin_Wcf_OperationWithToken_ClientApp.ServiceReference1.IService1
{
public Service1Client()
{
base.Endpoint.Behaviors.Add
(new RobinLib.AttachUserNamePasswordBehavior());
}
public Service1Client(string endpointConfigurationName) :
base(endpointConfigurationName)
{
base.Endpoint.Behaviors.Add
(new RobinLib.AttachUserNamePasswordBehavior());
}
public Service1Client
(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
base.Endpoint.Behaviors.Add
(new RobinLib.AttachUserNamePasswordBehavior());
}
public Service1Client
(string endpointConfigurationName,
System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
base.Endpoint.Behaviors.Add(new RobinLib.
AttachUserNamePasswordBehavior());
}
public Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
base.Endpoint.Behaviors.Add
(new RobinLib.AttachUserNamePasswordBehavior());
}
public string GetData(int value)
{
return base.Channel.GetData(value);
}
public Robin_Wcf_OperationWithToken_ClientApp.ServiceReference1.
CompositeType GetDataUsingDataContract
(Robin_Wcf_OperationWithToken_ClientApp.
ServiceReference1.CompositeType composite)
{
return base.Channel.GetDataUsingDataContract(composite);
}
到此,代碼基本實(shí)現(xiàn)了,在正式應(yīng)用的時(shí)候,我們只需要為每個(gè)客戶端創(chuàng)建獨(dú)立的用戶名,密碼對(duì),然后將這個(gè)信息通過(guò)一些渠道告訴服務(wù)消費(fèi)者,服務(wù)消費(fèi)者需要將用戶名,密碼放到Web.Config中的AppSettings中。而且在正式應(yīng)用的時(shí)候,需要將放置到MessageHeader中的用戶名,密碼進(jìn)行加密,而不是明文傳輸。這樣這套機(jī)制就能用于生產(chǎn)啦。
通過(guò)這種辦法,我們能為每個(gè)操作都設(shè)定身份驗(yàn)證,同時(shí)不需要更改Operation函數(shù)內(nèi)容和客戶端調(diào)用方式,我們來(lái)看一下運(yùn)行結(jié)果:
用戶,密碼正確情況下的調(diào)用
服務(wù)器端:
客戶端:
如果用戶名,密碼不匹配,服務(wù)能正常運(yùn)行,但客戶端會(huì)遇到異常
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客轉(zhuǎn)載