惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Help Net Security
Help Net Security
G
Google Developers Blog
雷峰网
雷峰网
WordPress大学
WordPress大学
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Engineering at Meta
Engineering at Meta
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
AWS News Blog
AWS News Blog
F
Full Disclosure
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Exploit Database - CXSecurity.com
J
Java Code Geeks
U
Unit 42
C
Cyber Attacks, Cyber Crime and Cyber Security
V
V2EX
C
Cisco Blogs
博客园 - 司徒正美
Project Zero
Project Zero
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
Blog — PlanetScale
Blog — PlanetScale
Scott Helme
Scott Helme
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog
S
Securelist
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
S
Schneier on Security
G
GRAHAM CLULEY
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
MongoDB | Blog
MongoDB | Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
T
Threatpost
Recorded Future
Recorded Future
C
CXSECURITY Database RSS Feed - CXSecurity.com
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
The Register - Security
The Register - Security
S
Security Archives - TechRepublic
博客园 - Franky
N
News | PayPal Newsroom
Simon Willison's Weblog
Simon Willison's Weblog
S
SegmentFault 最新的问题
W
WeLiveSecurity
A
Arctic Wolf
B
Blog

博客园 - ph580

国外大牛开发者创造出Siri第三方服务器 HTML5 开发者需要了解的技巧你要知道的知识 VS2008几大新功能描述如下 VS2008和.NET Framework 3.5功能与区别特性 竞争情报的战略与战术运用会让公司得利 为何中国企业在同一个问题上跌倒 开发嵌入式WEB的网络视频监控系统,设计思路 LightSwitch数据源开发小例 Amoeba新版本如何与MYSQL读写分离配置 SQL server 2005 master数据库进行轻型的恢复备份操作 C#多线程窗体控件安全访问实现方法 安全、简单的Windows Forms多线程编程实现 如何下手进行高效的c#线程池设计 C#编程开发 Berkeley DB SQL使用方法 装箱和拆箱(什么是装箱和拆箱) C#基础 JavaScript 正则表达式解析常用方法 C#正则表达式无法识别双引号解决 ASP.NET服务器控件开发之实现事件分析 Asp.net控件开发学习之数据回传小节
WCF中使用SoapHeader进行验证实现方法总结
ph580 · 2011-07-12 · via 博客园 - ph580

本文介绍一下在WCF中使用SoapHeader进行验证的两种实现方法,同时再次复习自定义Inspector和自定义EndpointBehavior。

  在Xml Web Service中能将用户的身份信息如用户名,密码添加到SoapHeader中,从而实现服务调用的身份验证,这种做法是沿用了Http中用户名,密码身份验证,是我们最乐于接受的。而在WCF中因为提供了非常健壮的安全机制,但实现起来真是不够简单。对于多数应用情景来讲,有点大炮打蚊子的感觉。因此好多人在网上询问在WCF中如何象XMl Web Service一样使用SoapHeader来完成用户名,密码身份验证。传统的办法是通过在服务的操作中从OperationContext.Current.IncomingMessageHeaders来获取Header中的内容,而在客户端在OperationContext.Current.OutgoingMessageHeaders中添加MessageHeader。下面的代码片段简要的介绍了这种实现:

在服务端的一个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", "http://tempuri.org");

if (index >= 0)

{

username =

OperationContext.Current.IncomingMessageHeaders.

GetHeader<string>(index).ToString();

}

index = OperationContext.Current.IncomingMessageHeaders.

FindHeader("pwd", "http://tempuri.org");

if (index >= 0)

{

pwd = OperationContext.Current.IncomingMessageHeaders.

GetHeader<string>(index).ToString();

}

return string.Format("You entered: {0}", value);

}

在客户端调代码如下:

Robin_Wcf_Formatter_Svc.Service1Client svc =

new Robin_Wcf_Formatter_Svc.Service1Client();

using (OperationContextScope scope =

new OperationContextScope(svc.InnerChannel))

{

MessageHeader header =

MessageHeader.CreateHeader("username", "http://tempuri.org", "robinzhang");

OperationContext.Current.OutgoingMessageHeaders.Add(header);

header = MessageHeader.CreateHeader("pwd", "http://tempuri.org", "robinzhang");

OperationContext.Current.OutgoingMessageHeaders.Add(header);

string res = svc.GetData(10);

}

通过上边的代码实现,已经能在WCF中使用pdfSoapHeader来传递身份信息了。但这种方式需要在每次客户端调用和每个服务操作中都增加类似代码片断。比较麻烦。多数情况下,我们的服务开发好之后,往往只开放给固定的用户用于消费,如果我们的服务的实例模式为PerCallpdf,也就是不保存会话,同时我们又希望能验证调用者的身份信息,我们需要在每个Operation的消息中增加SoapHeader来附加身份信息。这样服务即可保证每一个操作都不被非法调用。阅读完上篇文章,已经了解到通过MessageInspector能拦截消息用于记录或者修改,如果在拦截到消息之后,在消息中增加MessageHeader便可以实现上述需求。为此我们实现了一个实现IClientMessageInspector, IDispatchMessageInspector, IEndpointBehavior三个接口的类,这样该类就承担了两种角色,自定义MessageInspector,自定义EndpointBehavior。这个类的代码如下:

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", "http://tempuri.org", UserName, false, "");

MessageHeader pwdNameHeader =

MessageHeader.CreateHeader

("OperationPwd", "http://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, "http://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通过pdf代码方式应用到Host和Proxy中

服务宿主程序

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)

{

//服务地址

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 });

//服务绑定

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("http://127.0.0.1:8001/Mex");

host.Description.Behaviors.Add(svcMetaBehavior);

}

host.Opened += new EventHandler(delegate(object obj, EventArgs e)

{

Console.WriteLine("服务已经启动!");

});

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 Service1Clientpdf(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_Wcfpdf_OperationWithToken_ClientApp.ServiceReference1.

CompositeType GetDataUsingDataContract

(Robin_Wcf_OperationWithToken_ClientApp.

ServiceReference1.CompositeType composite)

{

return base.Channel.GetDataUsingDataContract(composite);

}

到此,代码基本实现了,在正式应用的时候,我们只需要为每个客户端创建独立的用户名,密码对,然后将这个信息通过一些渠道告诉服务消费者,服务消费者需要将用户名,密码放到Web.Config中的AppSettings中。而且在正式应用的时候,需要将放置到MessageHeader中的用户名,密码进行加密,而不是明文传输。这样这套机制就能用于生产啦。