关于WEB Service&WCF&WebApi实现身份验证之WCF篇(2)
因前段时间工作变动(换了新工作)及工作较忙暂时中断了该系列文章,今天难得有点空闲时间,就继续总结WCF身份验证的其它方法。前面总结了三种方法(详见:关于WEB Service&WCF&WebApi实现身份验证之WCF篇(1)),今天又将分享三种方法,完成WCF篇。
第四种:SOAP Header验证
首先定义一个WCF服务契约及服务实现类(后面的各种验证均采用该WCF服务),我这里直接采用默认的代码,如下:
服务契约定义:
namespace WcfService1
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
[ServiceContract(Namespace="http://www.zuowenjun.cn")]
public interface IService1
{ [OperationContract]
string GetData(int value); [OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite); // TODO: 在此添加您的服务操作
} // 使用下面示例中说明的数据约定将复合类型添加到服务操作。
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello "; [DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
} [DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
服务实现Service1.svc:
namespace WcfService1
{
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
} public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
然后就开始编写实现SOAP Header验证的具体逻辑,为了便于扩展及可移植性,我这里新建一个类库项目:WcfServiceBehaviorExtension,在该项目中定义三个类,第一个是实现自IClientMessageInspector的类:AttachUserNamePasswordInspector,用于实现客户端在调用WCF服务时自动附加用户信息(用户名及密码);第二个是实现自IDispatchMessageInspector, IEndpointBehavior的类:ValidateUserNamePasswordBehavior,用于实现自定义消息分发及验证用户信息;第三个是继承自BehaviorExtensionElement的类:ValidateUserNamePasswordElement,用于实现endpointBehavior扩展,可直接配置在config文件中,若采用在代码动态添加ValidateUserNamePasswordBehavior则这类可以不用定义,下面依次分享这三个类的实现代码。
AttachUserNamePasswordInspector
namespace WcfServiceBehaviorExtension
{
public class AttachUserNamePasswordInspector : IClientMessageInspector
{
private static string userName = System.Configuration.ConfigurationManager.AppSettings["username"];
private static string password = System.Configuration.ConfigurationManager.AppSettings["pwd"]; 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://www.zuowenjun.cn", userName, false, "");
MessageHeader pwdNameHeader = MessageHeader.CreateHeader("OperationPwd", "http://www.zuowenjun.cn", password, false, ""); request.Headers.Add(userNameHeader);
request.Headers.Add(pwdNameHeader);
return null;
}
}
}
ValidateUserNamePasswordBehavior
namespace WcfServiceBehaviorExtension
{ public class ValidateUserNamePasswordBehavior : IDispatchMessageInspector, IEndpointBehavior
{ #region IDispatchMessageInspector 成员 private string GetHeaderValue(string key)
{ int index = OperationContext.Current.IncomingMessageHeaders.FindHeader(key, "http://www.zuowenjun.cn"); 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)
{
string username = GetHeaderValue("OperationUserName");
string pwd = GetHeaderValue("OperationPwd"); if (!(username == "admin" && pwd == "wcf.admin"))
{
throw new FaultException("操作的用户名或密码不正确!");
}
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 AttachUserNamePasswordInspector());
} public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new ValidateUserNamePasswordBehavior());
} public void Validate(ServiceEndpoint endpoint)
{
} #endregion } }
ValidateUserNamePasswordElement
namespace WcfServiceBehaviorExtension
{
public class ValidateUserNamePasswordElement: BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(ValidateUserNamePasswordBehavior); }
} protected override object CreateBehavior()
{
return new ValidateUserNamePasswordBehavior();
}
}
}
定义好上述类后,我们再在WCF服务项目:WcfService1中引用该项目,然后修改配置文件web.config:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="soapHaderValidation" type="WcfServiceBehaviorExtension.ValidateUserNamePasswordElement,WcfServiceBehaviorExtension"/>
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="service1behavior">
<soapHaderValidation />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WcfService1.Service1">
<endpoint binding="wsHttpBinding" contract="WcfService1.IService1" behaviorConfiguration="service1behavior"></endpoint>
</service>
</services>
</system.serviceModel>
注意以下几点:
1.配置extensions > behaviorExtensions节点,里面添加ValidateUserNamePasswordElement类型信息,name为自定义名称,type为ValidateUserNamePasswordElement的完整类型名称,中间加逗号分隔,后面是该类所在的程序集名称;
2.配置endpointBehaviors > behavior节点,增加1中命名的节点soapHaderValidation,这里可能会报错误,但请不要理会,因为该节点在运行时才会生效的。
3.配置endpoint节点,需要添加behaviorConfiguration属性,并指定到2中所相对应的behavior
上述配置说白了就是配置自定义的服务行为类,使用在运行时注入到WCF服务管道中。
服务端设置好后并发布运行,最后就是客户端引用该WCF服务,同时需要引用WcfServiceBehaviorExtension项目,然后修改配置文件app.config(我这里演示采用的是控制台程序):
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="soapHaderValidation" type="WcfServiceBehaviorExtension.ValidateUserNamePasswordElement,WcfServiceBehaviorExtension"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="service1behavior">
<soapHaderValidation />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:7704/Service1.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference.IService1"
name="WSHttpBinding_IService1" behaviorConfiguration="service1behavior">
</endpoint>
</client>
</system.serviceModel>
注意事项与服务端配置文件相同,即需要配置extensions > behaviorExtensions节点、endpointBehaviors > behavior节点、endpoint节点添加behaviorConfiguration属性
另还需在appSettings节点中增加配置用户名及密码
配置好后就可以在程序中调用WCF服务,调用方法过程与普通的调用方法相同,并无其它区别。当然如果不想在客户端配置自定义节点及,可以通过以下代码来动态的添加用户名及密码信息:
static void Main(string[] args)
{ try
{
var client = new ServiceReference.Service1Client();
using (OperationContextScope contextScope = new OperationContextScope(client.InnerChannel))
{
AppendUserInfoHeader("admin", "wcf.admin");
string str = client.GetData(1);
Console.WriteLine(str);
}
}
catch (Exception ex)
{
Console.WriteLine("ERROR:{0}", ex.Message);
} Console.WriteLine("我是主线程!");
Console.Read();
} static void AppendUserInfoHeader(string username, string pwd)
{
MessageHeader header = MessageHeader.CreateHeader("OperationUserName", "http://www.zuowenjun.cn", username);
OperationContext.Current.OutgoingMessageHeaders.Add(header); header = MessageHeader.CreateHeader("OperationPwd", "http://www.zuowenjun.cn", pwd);
OperationContext.Current.OutgoingMessageHeaders.Add(header); }
第五种:集成Windows用户组授权与认证
首先,在实现服务契约时,我们在需要授权与认证的类或方法中添加PrincipalPermission特性,代码如下:
[PrincipalPermission(SecurityAction.Demand,Role="Users")]
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
这里我配置的是只有当请求的WINDOWS凭证的用户角色类型为Users才允许调用该方法,可以设置成其它角色或指定用户名(Name)等。
然后修改配置文件,配置如下:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="service1behavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceAuthorization principalPermissionMode="UseWindowsGroups"></serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WcfService1.Service1" behaviorConfiguration="service1behavior">
<endpoint name="service1" binding="wsHttpBinding" contract="WcfService1.IService1" ></endpoint>
</service>
</services>
</system.serviceModel>
这里需要注意,需配置serviceBehaviors > behavior > serviceAuthorization节点,将principalPermissionMode属性设为:UseWindowsGroups,并同时在service节点上指定behaviorConfiguration即可。
最后客户端正常引用该WCF服务,然后在调用WCF服务时,需要创建WindowsClientCredential对象,并指定用户名与密码,这里的用户名与密码均为该WCF服务端上服务器上的WINDOWS用户名及密码,具体代码如下:
static void Main(string[] args)
{ try
{
var client = new ServiceReference.Service1Client();
NetworkCredential credential = client.ChannelFactory.Credentials.Windows.ClientCredential;
credential.UserName = "test";
credential.Password = "test"; string str = client.GetData(1);
Console.WriteLine(str); ; }
catch (Exception ex)
{
Console.WriteLine("ERROR:{0}", ex.Message);
} Console.WriteLine("我是主线程!");
Console.Read();
}
第六种:集成Windows验证
服务定义与实现与文章开头贴出的代码相同,无任何特殊的地方,唯一的区别在于配置文件,如下:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="service1binding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WcfService1.Service1" >
<endpoint name="service1" binding="basicHttpBinding" bindingConfiguration="service1binding" contract="WcfService1.IService1" ></endpoint>
</service>
</services>
</system.serviceModel>
注意事项:
1.绑定类型,我这里采用basicHttpBinding(不是每个绑定都支持WINDOWS验证的),同时需配置该绑定类型的security节点,mode设为TransportCredentialOnly,transport.clientCredentialType设为Windows。
完成上述配置后再直接访问WCF服务地址时,弹出需要输入用户名及密码,就表明WINDOWS验证已生效,接下来就是客户端引用该WCF服务,引用后配置文件自动进行了相应的更新,这里我们无需修改,然后在调用WCF服务时需要传入WindowsClientCredential对象,并指定用户名与密码,这里的用户名与密码均为该WCF服务端上服务器上的WINDOWS用户名及密码,与第五种相同代码就不贴出来了。
关于WEB Service&WCF&WebApi实现身份验证之WCF篇(2)的更多相关文章
- 关于WEB Service&WCF&WebApi实现身份验证之WCF篇(1)
WCF身份验证一般常见的方式有:自定义用户名及密码验证.X509证书验证.ASP.NET成员资格(membership)验证.SOAP Header验证.Windows集成验证.WCF身份验证服务(A ...
- 关于WEB Service&WCF&WebApi实现身份验证之WebApi篇
之前先后总结并发表了关于WEB Service.WCF身份验证相关文章,如下: 关于WEB Service&WCF&WebApi实现身份验证之WEB Service篇. 关于WEB S ...
- 关于WEB Service&WCF&WebApi实现身份验证之WEB Service篇
在这个WEB API横行的时代,讲WEB Service技术却实显得有些过时了,过时的技术并不代表无用武之地,有些地方也还是可以继续用他的,我之所以会讲解WEB Service,源于我最近面试时被问到 ...
- ASP.NET WEBAPI 的身份验证和授权
定义 身份验证(Authentication):确定用户是谁. 授权(Authorization):确定用户能做什么,不能做什么. 身份验证 WebApi 假定身份验证发生在宿主程序称中.对于 web ...
- 无法在Web服务器上启动调试,与Web服务器通信时出现身份验证错误
问题描述: 我使用的是修改hosts,模拟真实网址来进行调试的.具体是这样的:我修改hosts文件,把某个域名,如www.163.com映射为127.0.0.1,然后在IIS信息管理器中,创建一个网站 ...
- Azure Service Bus 中的身份验证方式 Shared Access Signature
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- C# 实现身份验证之WCF篇(1)
WCF身份验证一般常见的方式有:自定义用户名及密码验证.X509证书验证.ASP.NET成员资格(membership)验证.SOAP Header验证.Windows集成验证.WCF身份验证服务(A ...
- WebApi 登录身份验证
前言:Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能,一个功能复杂的业务应用系统,通过角色授权来控制用户访问,本文通过Form认证,Mvc的Controller基类及Action的权 ...
- c# WebApi之身份验证:Basic基础认证
为什么需要身份认证 身份认证是为了提高接口访问的安全性,如果没有身份验证,那么任何匿名用户只要知道服务器的url,就可以随意访问服务器,从而访问或者操作数据库,这会是很恐怖的事. 什么是Basic基础 ...
随机推荐
- 如何禁用Marlin温度保护
最近在玩3D打印,搞了套MEGA 2560 + RAMPS 1.4 + A4988,刷Marlin(https://github.com/MarlinFirmware/Marlin)固件,接上电机调试 ...
- 如何利用Github+Appveyor+Nuget打造自己的.net core开源库
以下教程基于你有一个托管在Github上的.net core项目,如果没有的可以自己fork一个或者自己创建了默认的项目即可. 我们打开需要生成nuget包的项目中的project.json文件,有关 ...
- select * from table 时间长
优化中发现一个存储过程执行20秒通过profiler 抓取发现时间主要消耗在一个select * from 表,那么问题来了select几万数据竟然花了将近20秒? 问题排查清了程序前端使用了data ...
- 统计第一个空字符前面的字符长度(java实现)
举例来说:char buf[] = {'a','b','c','d','e','f','\0','x','y','z'}当输入N=10或20,期待输出是6:当输入N=3或5,期待输出是3或5. pac ...
- Lock,LockFree,MemoryBarrier,ConcurrentCollection
最近看并行编程书本的一些心得,简单记录下多线程和并行编程必知必会的几个概念,再次加深自己的理解. .NET Framework4提供了一个新的命名空间System.Collections.Concur ...
- Springlake-02 权限&文档设置&Role设置&Folder设置&登录
1. 权限 有3个默认的权限用户: 1.System Owner so 管理员权限全部:Type Setup; Group Setup; Form Setup; Role Setup; Share R ...
- 说说设计模式~适配器模式(Adapter)
返回目录 之前和大家一起谈了工厂模式和单例模式,今天来看一下另一种非常常用的模式,它就是适配器模式,第一次看到这个模式是通过“张逸”老师的“设计之道”这篇文章,在这里表adapter讲的很透彻,今天把 ...
- MVVM架构~knockoutjs系列之正则表达式使规则更灵活
返回目录 几乎每种验证架构都会有正则表达式的加盟,一般地,一种验证架构首先会提供一些标准的,常用的验证规则,它们通常是数字验证,电话验证,email验证,长度验证,范围验证,日期验证等,而如果使你的验 ...
- Paip.语义分析----情绪情感词汇表总结
Paip.语义分析----情绪情感词汇表总结 以下词语是按感情色彩共分为十四类: 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:h ...
- 创建Cookie,简单模拟登录,记录登录名,购物车记录先前添加内容,session控制登录
工作任务:模拟淘宝登录和购物车功能:使用cookie记录登录名,下次登录时能够记得上次的登录名,使用cookie模拟购物车功能,使用session记住登录信息并验证是否登录,防止利用url打开网站, ...